合理使用线程池能够带来3个好处:

1)降低资源消耗;2)提高响应速度;3)提高线程的可管理性。

那么线程池是如何工作的呢,借用并发编程艺术一书中的话来描述当一个任务提交给线程池之后,线程池会怎么做?

首先,线程池会判断核心线程池里的线程(线程总数是30,则coreSize有可能是10)是否都在执行任务。如果没有比方说当前只有9个线程在工作,则从核心线程池中创建一个新的线程来执行任务。如果当前已经有10个线程在工作了,则进入下一步;

其次,线程池会判断工作队列是否已经满了,如果工作队列没有满,则将新提交的任务存储在这个工作队列里,如果工作队列已经满了,则进入下一个流程;

最后,线程池判断全部线程是否都在工作,如果没有,即30个线程只有25个在工作,则创建一个新的工作线程来执行任务,如果已经有30个线程来执行,没有任何空闲线程,

则交给饱和策略来处理这个任务(默认的饱和策略为抛异常)。

下面我们借用书中的图来说明:

线程池的执行流程_第1张图片

从图种可以看出,提交任务之后,首先会尝试着交给核心线程池中的线程来执行,但是必定核心线程池中的线程数有限,所以必须要由任务队列来做一个缓存,先将任务放队列中缓存,然后等待线程去执行,最后,由于任务太多,队列也满了,这个时候线程池中剩下的线程就会启动来帮助核心线程池执行任务。如果还是没有办法正常处理新到的任务,则线程池只能将新提交的任务交给饱和策略来处理了。

线程池的创建new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,milliseconds,

runnableTaskQueue,handler);

其中最后一个参数handler就是饱和策略,java线程池框架提供的4种可策略:

1)AbortPolicy:直接抛出异常

2)CallerRunsPolicy:只用调用者所在线程来运行任务

3)DiscardOldestPolicy:丢弃队列中最近的一个任务,并执行当前任务。

4)DiscardPolicy:不处理,丢弃掉。

最后我们列举一个线程池max=5,core=3,任务队列taskQueue=5;采用饱和策略为1)

则我们看看提交任务给此线程池的执行逻辑如下:

1)首先我们提交第一个任务到线程池,此时核心线程数都还没有用,所以会启动核心线程之一来执行任务,记住为了说明这个流程,我们的任务的占用时间都很长,所以短时间内不会结束;

2)接着提交第二个第三个任务到线程池,他们的执行逻辑同第一个任务是一模一样的,线程池会启动核心线程池中剩下的两个线程来执行你新提交的任务。

3)接着又有新的任务提交过来,这个时候线程池发现核心线程池中的线程已经都在工作中,所以会去看任务队列taskQueue是否满了,发现并没有,是空的,所以将这个任务放入任务队列中等待核心线程池中有空闲线程时自己来取任务执行。

4)接着又提交了4个任务到线程池,他们分别判断核心线程是否空闲,不空闲,然后判断任务队列是否已满,不满,则直接将任务放入队列;

5)接着新的任务又来,则在判断核心线程池和任务队列之后,发现任务依然没有办法处理,则会判断是否线程数达到最大,发现没有,则新启动线程来执行任务;

6)接着又来一个任务,执行流程同5);

7)再来一个任务,发现核心线程池在忙,任务队列也满了,线程池中的全部线程也都在工作,没有办法处理他了,所以他找到了饱和策略,因为饱和策略是默认的抛异常,所以线程池会告诉提交任务的线程,已经没有可以用的线程了。

以上就一个核心线程数是3,总线程数是5,任务队列长度为5,默认策略采用抛异常的策略的从最开始到最后线程池满负荷运作的过程。