计算机系统应用教程网站

网站首页 > 技术文章 正文

Executors中各线程池的区别 线程池threadpoolexecutor

btikc 2024-10-22 10:31:59 技术文章 6 ℃ 0 评论

在 Java 中一共有 5 种线程池,分别是:CachedThreadPool,FixedThreadPool,SingleThreadExecutor,ScheduleThreadPool,ScheduledThreadPoolExecutor。它们分别是通过 Executors 的静态方法创建出来的。而他们底层是通过 ThreadPoolExecutor 类创建出来的。创建线程池时会传入以下参数,分别是:

  • corePoolSize:核心线程池的大小,在线程池被创建之后,其实里面是没有线程的。(当然,调用 prestartAllCoreThreads() 或者 prestartCoreThread() 方法会预创建线程,而不用等着任务的到来)。当有任务进来的时候,才会创建线程。当线程池中的线程数量达到corePoolSize之后,就把任务放到缓存队列当中。(就是 workQueue )。
  • maximumPoolSize:最大线程数量是多少。它标志着这个线程池的最大线程数量。如果没有最大数量,当创建的线程数量达到了 某个极限值,到最后内存肯定就爆掉了。
  • keepAliveTime:当线程没有任务时,最多保持的时间,超过这个时间就被终止了,默认值 60 秒。默认情况下,只有线程池中线程数量大于 corePoolSize 时,keepAliveTime 值才会起作用。也就说,只有在线程池线程数量超出 corePoolSize 了。我们才会把超时的空闲线程给停止掉。否则就保持线程池中有 corePoolSize 个线程就可以了。
  • Unit:参数keepAliveTime的时间单位,就是 TimeUnit类当中的几个属性。
  • workQueue:用来存储待执行任务的队列,不同的线程池它的队列实现方式不同(因为这关系到排队策略的问题)比如有以下几种:

ArrayBlockingQueue:基于数组的队列,创建时需要指定大小。

LinkedBlockingQueue:基于链表的队列,如果没有指定大小,则默认值是 Integer.MAX_VALUE。(newFixedThreadPool和newSingleThreadExecutor使用的就是这种队列),吞吐量通常要高于ArrayBlockingQuene。

SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene(newCachedThreadPool使用的就是这种队列)。

  • threadFactory:线程工厂,用来创建线程。通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名。
  • Handler:拒绝执行任务时的策略,一般来讲有以下四种策略:

ThreadPoolExecutor.AbortPolicy (默认的执行策略)丢弃任务,并抛出 RejectedExecutionException 异常。

ThreadPoolExecutor.CallerRunsPolicy:该任务被线程池拒绝,由调用 execute 方法的线程执行该任务。

ThreadPoolExecutor.DiscardOldestPolicy :抛弃队列最前面的任务,然后重新尝试执行任务。

ThreadPoolExecutor.DiscardPolicy,丢弃任务,不过也不抛出异常。


几种线程池的比较

0x01: CachedThreadPool

优点:

工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。

如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

缺点:

在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。


0x02:FixedThreadPool

创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。定长线程池的大小最好根据系统资源进行设置如Runtime.getRuntime().availableProcessors()

优点:

FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。

缺点:

但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。


0x03:SingleThreadExecutor

创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。


0x04:ScheduleThreadPool

创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。


线程池任务的提交流程

如果当前线程池线程数目小于 corePoolSize(核心池还没满呢),那么就创建一个新线程去处理任务。

如果核心池已经满了,来了一个新的任务后,会尝试将其添加到任务队列中,如果成功,则等待空闲线程将其从队列中取出并且执行,如果队列已经满了,则继续下一步。

此时,如果线程池线程数量 小于 maximumPoolSize,则创建一个新线程执行任务,否则,那就说明线程池到了最大饱和能力了,没办法再处理了,此时就按照拒绝策略来处理。(就是构造函数当中的 Handler 对象)。

如果线程池的线程数量大于 corePoolSize,则当某个线程的空闲时间超过了 keepAliveTime,那么这个线程就要被销毁了,直到线程池中线程数量不大于 corePoolSize 为止。


线程池的 shutdown 和 shutdownNow 方法的区别是什么

  • shutdown 设置状态为 SHUTDOWN,而 shutdownNow 设置状态为 STOP
  • shutdown 只中断空闲的线程,已提交的任务可以继续被执行,而 shutdownNow 中断所有线程
  • shutdown 无返回值,shutdownNow 返回任务队列中还未执行的任务

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表