java线程池的工作过程_JAVA线程池原理源码解析—为什么启动一个线程池,提交一个任务后,Main方法不会退出?...

public static void main(String[] args) {

ExecutorService service = Executors.newFixedThreadPool(10);

service.submit(() -> System.out.println("Hello "));

System.out.println("World");

}

呵呵,执行结果谁都知道,显而易见

59c04e26b98b142168d78bcbae38bf41.png

结论

线程池的创建的时候,第一次submit操作会创建Worker线程(负责去拿任务处理),该线程里写了一个死循环,所以这个Worker线程不会死

Worker线程在创建的时候,被设置成了非守护线程,thread.setDaemon(false)

早在JDK1.5的时候,就规定了当所有非守护线程退出时,JVM才会退出,Main方法主线程和Worker线程都是非守护线程,所以不会死。

下面我们会就上面几个问题,每一个问题进行源码分析,感兴趣的看官老爷可以继续,看看又不会掉发(逃

源码分析

为什么Worker线程不会死

梦开始的地方先从初始化开始

//该方法利用多台实例化了一个ThreadPoolExecutor线程池,该线程池继承了一个抽象类AbstractExecutorService

ExecutorService service = Executors.newFixedThreadPool(10);

//调用了ThreadPoolExecutor.submit方法也就是父类的AbstractExecutorService.submit,该方法内部会去调用execute()方法

service.submit(() -> System.out.println("Hello "));

于是我们定位到ThreadPoolExecutor类的execute方法,我截取了部分如下,注意代码中我打注释的地方

public void execute(Runnable command) {

...

//如果工作线程还没有超过核心线程数

if (workerCountOf(c) < corePoolSize) {

//去添加工作线程

if (addWorker(command, true))

return;

}

...

线程池把每一个运行任务的工作线程抽象成了Worker,我们定位到内部addWorker方法

...

//新建一个Worker

w = new Worker(firstTask);

final Thread t = w.thread;

if (t != null) {

//下面的操作是将线程添加到工作线程集合里

final ReentrantLock mainLock = this.mainLock;

mainLock.lock();

try {

int rs = runStateOf(ctl.get());

if (rs < SHUTDOWN ||

(rs == SHUTDOWN && firstTask == null)) {

if (t.isAlive()) // precheck that t is startable

throw new IllegalThreadStateException();

workers.add(w);

int s = workers.size();

if (s > largestPoolSize)

largestPoolSize = s;

workerAdded = true;

}

} finally {

mainLock.unlock();

}

//如果添加成功的话

if (workerAdded) {

//把工作线程跑起来

t.start();

workerStarted = true;

}

}

} finally {

if (! workerStarted)

addWorkerFailed(w);

}

return workerStarted;

这时候一个工作线程也就跑起来了,可以去执行任务了,我们定位到ThreadPoolExecutor的内部类Worker的run方法里

//该类调用了runWorker方法

public void run() {

runWorker(this);

}

final void runWorker(Worker w) {

Thread wt = Thread.currentThread();

Runnable task = w.firstTask;

w.firstTask = null;

w.unlock(); // allow interrupts

boolean completedAbruptly = true;

try {

//主要看这个while,会看这个Worker有没有任务,如果没有就会去取,这里是一个死循环,然后我们定位到getTask()方法,看他是怎么取任务的

while (task != null || (task = getTask()) != null) {

w.lock();

if ((runStateAtLeast(ctl.get(), STOP) ||

(Thread.interrupted() &&

runStateAtLeast(ctl.get(), STOP))) &&

!wt.isInterrupted())

wt.interrupt();

...

}

这里解释了,工作线程其实不会死(超时时间不在本期范围内),我们继续定位到内部的getTask()方法,看他是怎么取任务的

private Runnable getTask() {

...

//有没有设置核心线程超时时间(默认没有)当前工作的线程数大于了线程池的核心线城市

boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

...

try {

Runnable r = timed ?

workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :

//调用workQueue的Take方法,WorkQueue默认是一个BlockingQueue,所以调用take方法会导致当前工作线程阻塞掉,指到拿到

workQueue.take();

//如果拿到任务就返回

if (r != null)

return r;

timedOut = true;

...

小结:

这里想说的有两点:

工作线程不会死(不设置线程存活时间,默认情况下),会一直拿任务,所以工作线程会一直活着

工作线程拿任务的时候,默认情况下,因为用的是BlockingQueue的take()拿不到任务会阻塞

Worker线程如何被设置成非守护线程

首先我们来到ThreadPoolExecutor的构造方法里

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue workQueue) {

this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,

Executors.defaultThreadFactory(), defaultHandler);

}

构造器里传入了一个ThreadFactory也就是Executors.defaultThreadFactory(),用来产生工作线程,一步一步的点进去我们会定位到Executors内部类DefaultThreadFactory的newThread方法

public Thread newThread(Runnable r) {

Thread t = new Thread(group, r,

namePrefix + threadNumber.getAndIncrement(),

0);

//关键代码是这里,把线程设置成了非守护线程

if (t.isDaemon())

t.setDaemon(false);

if (t.getPriority() != Thread.NORM_PRIORITY)

t.setPriority(Thread.NORM_PRIORITY);

return t;

}

然后我们看ThreadPoolExector方法去new Worker()的时候

Worker(Runnable firstTask) {

setState(-1); // inhibit interrupts until runWorker

this.firstTask = firstTask;

//这里的ThreadPool,就是上面提到的那个生产非守护线程的线程工厂

this.thread = getThreadFactory().newThread(this);

}

看上面的注释下面的内容,为什么是非守护线程就真相大白了。

为什么要等到非守护线程全部结束的时候,JVM才会退出?

4be924f1b15da356dfa459c54540338b.png

网上冲浪JdkDoc注意我标蓝的部分,这跟jvm的实现有关

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值