当前位置: 首页 > 科技观察

Executors被开发者抛弃有什么问题?

时间:2023-03-13 16:05:45 科技观察

一、前言在Java领域,我们使用多线程来实现并发编程。线程本身就是操作系统的一个概念。虽然不同的语言对线程有一定的封装,但最终都是调用到操作系统中来创建和调度线程。由于线程是一种重要的系统资源,为了更合理地利用这种资源,我们会使用池化技术来优化线程的创建和销毁,这就是线程池。我们在学习并发编程的时候,可以使用Thread创建线程,通过start()启动一个线程,但是在成熟的项目中,基本上是不允许这样操作线程的,需要使用线程池来收敛线程使用,所以线程池是必须的。Java的线程池可以通过ThreadPoolExecutor来构建,ThreadPoolExecutor提供了非常完善的构建方法,可以根据我们的业务需要灵活的构建线程池。同时Java还提供了一个Executors,它提供了很多封装方法,使用它可以帮助我们快速搭建线程池。Executors的初衷是为了让我们更方便的使用线程池,但是《阿里巴巴Java开发手册》也明确的指出了直接使用Executors的缺点。手册中提到,强制不使用Executors创建线程池,而是退化为最原始的ThreadPoolExecutor方法。在日常开发中,线程池的创建要抓紧,开发者要明确线程池的运行规则,尽可能规避资源耗尽的风险。线程池是个好东西,但是怎么创建是个问题。2.执行者怎么了?2.1不允许使用Executors的原因不应该使用。其实在《阿里巴巴Java开发手册》中已经说过,当需要处理大量任务时,可能会出现OOM异常,但它们OOM的原因也不尽相同。ThreadPoolExecutor的构造方法提供了很多参数配置,其中与Executors的OOM相关的有两个:核心线程数和等待队列。首先我们来看一下FixedThreadPool和SingleThreadPool发生OOM的原因。他们的问题是等待队列使用了LinkedBlockingQueue,一个用链表实现的无界队列(最大长度为Integer.MAX_VALUE),最终导致大量等待处理的任务堆积,从而导致频繁的GC并最终触发OOM。java.lang.OutOfMemoryError:GCoverheadlimitexceeded我们来看看CachedThreadPool中OOM的原因。它的问题是核心线程数设置为Integer.MAX_VALUE,等待队列是一个SynchronousQueue。SynchronousQueue是一个没有数据缓冲的阻塞队列,极易被阻塞。当等待队列阻塞时,如果线程数还没有达到核心线程数的限制,线程池的策略是创建新的线程来处理新的任务。也就是说,是核心线程的数量和等待队列SynchronousQueue的结合,导致线程不断的跟随任务创建,直到触发OOM。java.lang.OutOfMemoryError:pthread_creat(1040KBstack)failed:TryagainScheduledThreadPool等待队列使用DelayedWorkQueue,原理类似,最终会导致创建大量线程抛出OOM。线程是一种系统资源,它的创建会带来内存开销。同时,操作系统对单个进程可以创建的线程数也有限制。在Android中,每个线程初始化都需要mmap一定量的堆内存。默认情况下,初始化一个线程需要大约mmap1MB的内存空间。同时,系统本身也会对每个进程可以创建的线程数设置一定的限制。这个限制在/proc/pid/limits中。不同的制造商对此限制有不同的限制。有可用内存,还是会抛OOM。2.2执行器有什么问题?Executors在任务过多时会导致资源耗尽,触发OOM。这就是它带来的危害。Executors最大的问题是它们没有边界。当系统环境较好,任务不多时,Executors创建的线程池可以正常工作。但是一旦压力很大,我们就无法预料什么时候会出现问题。这意味着没有边界,没有边界意味着无法控制。我们很难相信一段无法控制的代码。什么时候会出错,完全不可预测。这是Executors最大的问题。另外,Executors封装了太多线程池的细节,不推荐使用。比如,通常我们需要给线程池创建的线程起一个有意义的名字,方便出现异常时进行排查;再比如,对于线程池的拒绝策略,我们需要仔细定义是直接丢弃还是持久化延迟处理。使用线程池思考一个线程池的不同参数带来的策略细节是一个很好的开发习惯。3.总结在本文中,我们讨论了创建线程池。使用Executors创建的线程池存在OOM风险。ThreadPoolExecutor应该用于创建线程池。清晰的从业务的角度去配置线程池的不同参数,比如线程池、等待队列、拒绝策略等,今天就到这里,有什么问题欢迎留言讨论。这篇文章对您有帮助吗?留言转发,点击好看就是最大的支持,谢谢!