多线程——Java线程池原理深入
Posted 小兀哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程——Java线程池原理深入相关的知识,希望对你有一定的参考价值。
上次我们简单了解了一下什么是线程池以及Java中几种类型的线程池,今天我们来深入剖析一下线程池的原理。
1、构造
1. 线程池管理器(ThreadPoolManager):用于创建并管理线程池
2. 工作线程(WorkThread): 线程池中线程
3. 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。
4. 任务队列:用于存放没有处理的任务。提供一种缓冲机制。
注:线程池管理器至少有下列功能:创建线程池,销毁线程池,添加新任务
2、线程状态
新建状态(New):新创建了一个线程对象。
就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
3、执行流程
a.一个任务提交,如果线程池大小没达到corePoolSize,则每次都启动一个worker也就是一个线程来立即执行
b.如果来不及执行,则把多余的线程放到workQueue,等待已启动的worker来循环执行
c.如果队列workQueue都放满了还没有执行,则在maximumPoolSize下面启动新的worker来循环执行workQueue
d.如果启动到maximumPoolSize还有任务进来,线程池已达到满负载,此时就执行任务拒绝RejectedExecutionHandler
// 流程就是:没达到corePoolSize,创建worker执行,达到corePoolSize加入workQueue
// workQueue满了且在maximumPoolSize下,创建新worker,达到maximumPoolSize,执行reject
public void execute(Runnable command)
if (command == null)
throw new NullPointerException();
// 1:poolSize达到corePoolSize,执行3把任务加入workQueue
// 2:poolSize没达到,执行addIfUnderCorePoolSize()在corePoolSize内创建新worker立即执行任务
// 如果达到corePoolSize,则同上执行3
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command))
// 3:workQueue满了,执行5
if (runState == RUNNING && workQueue.offer(command))
if (runState != RUNNING || poolSize == 0)
// 4:如果线程池关闭,执行拒绝策略
// 如果poolSize==0,新启动一个线程执行队列内任务
ensureQueuedTaskHandled(command);
// 5:在maximumPoolSize内创建新worker立即执行任务
// 如果达到maximumPoolSize,执行6拒绝策略
else if (!addIfUnderMaximumPoolSize(command))
// 6:拒绝策略
reject(command); // is shutdown or saturated
4、线程销毁
keepAliveTime:代表的就是线程空闲后多久后销毁,线程的销毁是通过worker的getTask()来实现的。
一般来说,Worker会循环获取getTask(),如果getTask()返回null则工作线程worker终结,那我们再看看什么时候getTask()返回null
Runnable getTask()
for (;;)
try
int state = runState;
if (state > SHUTDOWN)
return null;
Runnable r;
if (state == SHUTDOWN) // Help drain queue
r = workQueue.poll();
else if (poolSize > corePoolSize || allowCoreThreadTimeOut)
// 在poolSize大于corePoolSize或允许核心线程超时时
// 阻塞超时获取有可能获取到null,此时worker线程销毁
r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
else
r = workQueue.take();
if (r != null)
return r;
// 这里是是否运行worker线程销毁的判断
if (workerCanExit())
if (runState >= SHUTDOWN)
// STOP或TERMINATED状态,终止空闲worker
interruptIdleWorkers();
return null; // 这里返回null,代表工作线程worker销毁
// 其他:retry,继续循环
catch (InterruptedException ie)
// On interruption, re-check runState
5、线程池关闭
平缓关闭 shutdown:这个方法会将runState置为SHUTDOWN,会终止所有空闲的线程,同时不再接受新的任务,而仍在工作的线程不受影响,所以队列中的任务人会被执行
立即关闭 shutdownNow:此方法将runState置为STOP,和shutdown方法的区别是,这个方法会终止所有的线程(取消所有正在执行和未执行的任务),所以队列中的任务也不会被执行了。
总结:
通过这次学习,我们不仅知道了线程池是如何管理线程,而且还了解了线程多种状态之间的转换,这样更加便于我们对线程池的理解。之后,我们还将继续学习关于多线程的知识,让我们每天都有成长。
以上是关于多线程——Java线程池原理深入的主要内容,如果未能解决你的问题,请参考以下文章