线程池

作者: 漂泊的胡萝卜 | 来源:发表于2018-08-15 20:45 被阅读0次

    线程池能够避免频繁创建销毁线程带来的性能损耗,同时还维护了线程池的一些统计信息。

    JDK中线程池的核心类是ThreadPoolExecutor,它的继承链如下

    ThreadPoolExecutor——>AbstractExecutorService——>ExecutorService——>Executor

    Executor是一个只包含execute方法的接口,它的作用是将Task的提交和Task的执行进行解耦(Netty设计了自己的线程模型,该接口在Netty中也大量使用)。

    ExecutorService提供了Executor和Task(借助Futrue接口)的生命周期的管理函数。

    下面,主要看ThreadPoolExecutor这个类。

    1.组成:

    ThreadPoolExecutor包含一个BlockingQueue(workQueue)用于存储任务,一个Set<Worker>(workers)用于存储执行任务的线程。

    2.重要初始参数:

    (1)int corePoolSize  线程池核心线程数目

    (2)int maximumPoolSize  线程最大线程数目

    默认情况下,线程池初始化后,是没有线程的,当execute第一个任务时创建第一个Worker(即创建线程);当线程数小于corePoolSize时,不管有没有空闲的线程,都会创建一个Worker并执行该任务;当线程数大于等于corePoolSize时,新来的任务会优先加入到workQueue,当workQueue满的时候,才会新创建Work线程,当线程数目等于maximumPoolSize且workQueue满的时候,会采用RejectedExecutionHandler根据拒绝策略处理该任务。

    (3)long keepAliveTime

    大于corePoolSize的空闲线程的最大idle时间,通过BlockingQueue的poll(long timeout, TimeUnit unit)实现。

    (4)TimeUnit unit

    (5)BlockingQueue workQueue

    阻塞队列,三种典型的选择,SynchronousQueue、LinkedBlockingQueue、ArrayBlockingQueue。

    SynchronousQueue:该队列没有容量只是在生产者消费者之间交换信息和任务,此情况下线程池maxsize通常无限制,但也要防止耗尽系统资源。

    LinkedBlockingQueue:无界的阻塞队列,maxsize失去作用,同样可能会耗尽系统资源。

    ArrayBlockingQueue:queuesize和maxpoolsize要合理配置,大的queuesize和小的maxpoolsize可以减少线程切换的损耗,但是可能会影响吞吐量,小的queuesize和大的maxpoolsize也可能会产生大量线程切换进而影响吞吐量。

    (6)ThreadFactory threadFactory

    为线程起名字,利于日志分析。

    (7)RejectedExecutionHandler

    拒绝策略,应用了策略模式,JDK实现了四种,1.抛出异常,2.直接丢弃,3.删除阻塞队列中最老的task,4.添加任务的线程执行该任务(进而减慢生产者速度,达到调节作用)。

    3.状态

    RUNNING: 接收新任务,并处理阻塞队列中的任务;

    SHUTDOWN: 不接收新任务,但处理队列中的任务;

    STOP:    不接收新任务,不处理队列中的任务,并中断所有正在执行的任务;

    TIDYING:  所有任务都被,worker数目为0,当状态变为TIDYING时,会执行terminated()钩子方法;

    TERMINATED: terminated() 方法完成。

    RUNNING -> SHUTDOWN: On invocation of shutdown(), perhaps implicitly in finalize()

    (RUNNING or SHUTDOWN) -> STOP:On invocation of shutdownNow()

    SHUTDOWN -> TIDYING:When both queue and pool are empty

    STOP -> TIDYING:When pool is empty

    TIDYING -> TERMINATED:When the terminated() hook method has completed

    4.源码分析

    ThreadPoolExecutor的核心方法是execute方法,其实现其实就是按照前面的执行策略,建立线程(Worker),或者将任务添加到阻塞队列,或者将任务交给拒绝策略。

    添加Work的过程需要对mainLock加锁(mainLock用于控制对Worker集合的访问)。

    Worker实现了Runnable接口,并继承了AbstractQueuedSynchronizer类(实现了一个不可重入锁,作主要控制work线程的中断,未启动的线程不能中断,执行shutdown方法时,先获取该work的锁才能中断该线程),并且它是ThreadPoolExecutor的内部类(这样它就能访问ThreadPoolExecutor的内部变量和方法,在某种程度上可以说是实现了多继承)。它持有一个线程,一个Runnable即第一个执行的任务,以及一个long表示完成任务的数目。

    Worker初始化的时候,state(AbstractQueuedSynchronizer的属性)为-1,避免对为启动的Work调用interrupt方法。

    run方法执行ThreadPoolExecutor的runWorker方法,该方法首先获得firstTask,并将其赋值为null使其得到回收,接着在while循环中,执行第一个任务,并不断从阻塞队列中获取任务执行,需要注意的是每次执行任务前都要调用Work实例的加锁方法,并检查线程池的状态(加锁是防止shutdown时,杀死还在执行任务的线程)。

    参考文章:

    https://www.cnblogs.com/trust-freedom/p/6681948.html#label_3_3

    相关文章

      网友评论

          本文标题:线程池

          本文链接:https://www.haomeiwen.com/subject/ijzeuftx.html