线程池原理解析一

2018-01-01  本文已影响0人  无聊夫斯基

讲解线程池原理之前,首先了解一下什么是线程池:

        线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到线程池中并等待下一次分配任务。

至于为什么要使用线程池?

        对于操作系统来说,频繁的创建销毁线程会消耗大量的资源。所以可以创建线程池来统一管理这些运行的线程。线程用于在需要执行大量异步任务的情况下,使用线程池减少了每个任务运行的负载。

了解完线程池后,接下来了解它是如何使用的:

        JDK1.5提供了Executor框架,开发者可以使用线程池工厂类Executors根据不同的需求创建各种不同形式的线程池。常用的有以下几种线程池:

        1、newCachedThreadPool:

newCachedThreadPool

        创建一个阻塞队列为SynchronousQueue的线程池。当调用execute方法时,将重用空闲的工作线程。如果没有工作线程可利用,则将创建新的工作线程并添加到线程池。空闲线程超过60s将会被回收。

        2、newFixedThreadPool:

newFixedThreadPool

        创建一个固定数量的工作线程的线程池。如果所有的工作线程在执行任务,新提交的任务将会在任务队列中等待,直到有工作线程可利用。一旦线程空闲下来就会被回收。

        3、newSingleThreadExecutor:

newSingleThreadExecutor

        创建只有一个工作线程的executor。(工作线程在线程池关闭之前,如果执行任务失败,则创建一个新的工作线程来替代旧的工作线程)。

其实大多数线程池的本质都是初始化一个ThreadPoolExecutor对象。

ThreadPoolExecutor构造方法

        corePoolSize:线程池核心线程数。

        keepAliveTime:空闲线程允许的最大空闲时间。

        defaultThreadFactory:用于创建线程的工厂方法。

        maximumPoolSize: 线程池可以容纳的最大线程数。

        workQueue:用于存放任务的阻塞队列,提交的任务需要实现Runnable接口。JDK提供了如下几种阻塞队列:

            1、ArrayBlockingQueue:可以阻止资源的浪费,但在不可估量负载的情况下,可能出现吞吐量下降的情况。

            2、LinkedBlockingQueue:当任务提交的速度大于线程处理任务的速度时,可能出现任务无限制提交的情况。

            3、SynchronousQueue:每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。

            4、PriorityBlockingQueue:插入的元素必须可比较。

        defaultHandler:用于处理被拒绝的任务。当线程数到达最大线程数并且阻塞队列达到饱和,execute方法会调用RejectedExecutionHandler的rejectedExecution处理被拒绝的任务。JDK提供如下几种拒绝策略:

            1、ThreadPoolExecutor.AbortPolicy:默认情况下执行,直接抛出RejectedExecutionException运行时异常。 

            2、CallerRunsPolicy:线程调用execute方法执行任务。这种策略提供了一个反馈机制减慢新任务的提交速度。

            3、DiscardPolicy:直接丢弃新提交的任务 。

            4、DiscardOldestPolicy:如果executor没有关闭,队列头的任务将会被丢弃,然后executor重新尝试执行任务(如果失败,则重复这一过程)。

            5、我们也可以自己定义RejectedExecutionHandler以适应特殊环境的需求。

然后来了解一下线程池状态。

线程池状态定义

        在ThreadPoolExecutor类中有个ctl变量用于显示线程池状态。其中AtomicInteger是一个提供原子操作的Integer类,保证多线程情况下ctl的变化是线程安全的。ctl是个神奇的变量,它的高3位用于显示线程池状态,低29位用于显示线程池中的线程数。

        1、RUNNING:接受新的任务,处理队列任务。

        2、SHUTDOWN:不再接受新的任务,处理队列任务。

        3、STOP:不再接受新任务,不处理队列任务,中断正在执行的任务线程。 

        4、TIDYING:所有的任务已经结束,任务线程为0,线程转换到TIDYING状态。

        5、TERMINATED:线程池已将结束,即terminated()方法执行完。

 随着时间的推移,线程池状态之间也是在发生着转化,以下是几种状态转化的条件。

        1、RUNNING -> SHUTDOWN(调用shudown方法) 

        2、RUNNING或者SHUTDOWN -> STOP(调用shutdown方法)

        3、SHUTDOWN -> TIDYING (当线程池和队列都为空)

        4、STOP -> TIDYING (当线程池为空)

        5、TIDYING -> TERMINATED (terminated方法执行完)

目前先对线程池做个大致的了解,对于如何调用addWorker,runWorker等方法留到下篇再解释。

        本篇文章主要是看到占小狼大神的《深入分析java线程池的实现原理》,然后想有样学样的总结一下线程池原理,同时看了一下线程池几个核心类的源码并且翻译了一下注释,有兴趣的小伙伴可以去看看注释。对于这篇总结,如果哪里写的不对,谢谢批评指正。

参考:https://www.jianshu.com/p/87bff5cc8d8c

上一篇 下一篇

猜你喜欢

热点阅读