废话不多说,先抛出题目:
现在有一个线程池,核心线程2,最大线程3,超时时间30s,阻塞队列大小10,先往里面放5个任务,每个任务sleep1秒钟,把线程数扩到最大值3,然后每三秒往线程池里面添加一个休眠一秒钟的任务,60秒后,这个线程池还存活多少个线程
再给出代码:
package com.xiang.thread;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.junit.Test;
import java.util.Date;
import java.util.concurrent.*;
/**
* @author Xiang
* @date 2023/5/25 - 17:03
*/
public class ThreadTest {
@Test
public void test() throws InterruptedException {
ThreadPoolExecutor executorService = new ThreadPoolExecutor(2, 3, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(2), new DefaultThreadFactory("test"),
new ThreadPoolExecutor.DiscardPolicy());
//每隔两秒打印线程池的信息
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledExecutorService.scheduleAtFixedRate(() -> {
System.out.println("=====================================thread-pool-info:" + new Date() + "=====================================");
System.out.println("CorePoolSize:" + executorService.getCorePoolSize());
System.out.println("PoolSize:" + executorService.getPoolSize());
System.out.println("ActiveCount:" + executorService.getActiveCount());
System.out.println("KeepAliveTime:" + executorService.getKeepAliveTime(TimeUnit.SECONDS));
System.out.println("QueueSize:" + executorService.getQueue().size());
}, 0, 2, TimeUnit.SECONDS);
try {
//同时提交5个任务,模拟达到最大线程数
for (int i = 0; i < 5; i++) {
executorService.execute(new Task());
}
} catch (Exception e) {
e.printStackTrace();
}
//休眠10秒,打印日志,观察线程池状态
Thread.sleep(10000);
//每隔3秒提交一个任务
while (true) {
Thread.sleep(3000);
executorService.submit(new Task());
}
}
static class Task implements Runnable {
@Override
public void run(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "-执行任务");
}
}
}
先给出我们的正常思路,最开始往里面推5个任务,将最大线程撑到3,之后,每3秒往里面推一个休眠1秒的任务,正常逻辑思维来说,最开始那五个任务执行完毕之后,只需要一个线程就可以满足后面的每3秒发布一个任务,这种情况下,核心线程存活,非核心线程销毁,30s过后,就只会保留2个核心线程。但是,上结果
结果是,60秒过后,甚至120s过后,三个线程都不会销毁,源码之下无秘密,最关键的在于他的阻塞队列。
首先我们需要知道不管是ArrayBlockQueue还是LinkedBlockQueue阻塞队列的offer
和take
是需要加锁的。
我这里给出线程池的核心代码,给出注释说明
// 这段代码是线程池的添加任务代码
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
public boolean offer(E e) {
return offerLast(e);
}
public boolean offerLast(E e) {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
return linkLast(node);
} finally {
lock.unlock();
}
}
private boolean linkLast(Node<E> node) {
// assert lock.isHeldByCurrentThread();
if (count >= capacity)
return false;
Node<E> l = last;
node.prev = l;
last = node;
if (first == null)
first = node;
else
l.next = node;
++count;
// 关键在于这里 notEmpty.signal(),有一个Condition的唤醒操作
// 这里会唤醒因为获取任务线程而等待的线程,也就是ReentrantLock.lock()的获得者
// 而其他锁竞争者则还是在AQS队列中等待,所以也就出现了之前结果图中的
notEmpty.signal();
return true;
}
这里是线程池的获取任务
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
boolean completedAbruptly = true;
try {
// do getTask()
while (task != null || (task = getTask()) != null) {
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
try {
task.run();
afterExecute(task, null);
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
private Runnable getTask() {
boolean timedOut = false;
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
// poll() 或者 take()
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
public E take() throws InterruptedException {
return takeFirst();
}
public E takeFirst() throws InterruptedException {
final ReentrantLock lock = this.lock;
// 核心 获取Lock锁,A,B,C三个线程只会有一个线程获取到锁
// 在获取到锁之后,相应的也只会有一个线程会获取到执行下面notEmpty.await()的执行权
lock.lock();
try {
E x;
while ( (x = unlinkFirst()) == null)
// 进行等待唤醒,因为现在没有任务执行
notEmpty.await();
return x;
} finally {
lock.unlock();
}
}