巧了嘛,这不是。顺序刚好是:
- Thread[test-1-3,5,main]
- Thread[test-1-2,5,main]
- Thread[test-1-1,5,main]
消费者这边我们大概摸清楚了,接着去看看生产者。
- java.util.concurrent.ThreadPoolExecutor#execute
线程池是在这里把任务放到队列里面去的。
而这个方法里面的源码是这样的:
其中signalNotEmpty()
最终会走到 doSignal 方法,而该方法里面会调用 transferForSignal 方法。
这个方法里面会调用 LockSupport.unpark(node.thred)
方法,唤醒线程:
而唤醒的顺序,就是等待队列里面的顺序:
所以,现在你知道当一个任务来了之后,这个任务该由线程池里面的哪个线程执行,这个不是随机的,也不是随便来的。
是讲究一个顺序的。
什么顺序呢?
Condition 里面的等待队列里面的顺序。
什么,你不太懂 Condition?
那还不赶紧去学?等着我给你讲呢?
本来我是想写一下的,后来发现《Java并发编程的艺术》一书中的 5.6.2 小节已经写的挺清楚了,图文并茂。这部分内容其实也是面试的时候的高频考点,所以自己去看看就好了。
先欠着,欠着
非核心线程怎么回收?
还是上面的例子,假设非核心线程就空闲了超过 30 秒,那么它是怎么被回收的呢?
这个也是一个比较热门的面试题。
这题没有什么高深的地方,答案就藏在源码的这个地方:
- java.util.concurrent.ThreadPoolExecutor#getTask
当 timed 参数为 true 的时候,会执行 workQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)
方法。
而 timed 什么时候为 true 呢?
- boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
allowCoreThreadTimeOut 默认为 false。
所以,就是看 wc > corePoolSize
条件,wc 是活跃线程数。此时活跃线程数为 3 ,大于核心线程数 2。
因此 timed 为 true。
也就是说,当前 workQueue 为空的时候,现在三个线程都阻塞 workQueue.poll 方法中。
而当指定时间后,workQueue 还是为空,则返回为 null。
于是在 1077 行把 timeOut 修改为 true。
进入一下次循环,返回 null。
最终会执行到这个方法:
- java.util.concurrent.ThreadPoolExecutor#processWorkerExit