文章目录
背景
juc是jdk的并发包,最核心的有三个类,本系列博客将逐一进行阐述,最后配上实战。
线程池源码走读
结构总览
诞生于jdk1.5,下面看一下类图。类的实现以两个“执行器”接口类为导向。
这里只看一下ThreadPoolExecutor的实现:
核心实现
构造过程
参数如下:
- corePoolSize,核心线程数
- maximumPoolSize,最大线程数
- keepAliveTime,非核心线程存活时间
- workQueue,任务对列
- threadFactory,线程工厂类(生产线程的地方,可以指定线程名称等参数)
- RejectedExecutionHandler,线程池和任务对列满了以后用到
执行方法submit过程
看一下submit(此方法在抽象父类里)执行的时序图(时序图在面向过程编程中非常重要,是必看的)
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
// 做了一个Future(可以通过future进行线程执行体的控制),然后调用多态的execute方法,让子类(这里的子类实现类就是ThreadPoolExecutor)去实现。
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
我们再来看execute。使用时序图去查看,出现了很深的调用,而且从类上来看,大部分是线程同步和锁,这些全部是为了线程的正常执行而服务。
// 线程池的数据全部浓缩到了一个原子的int上,即ctl
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);
execute过程大致可分为三个过程:
- 如果执行线程数小于核心线程数,直接添加核心线程数
- 如果执行线程数小于核心线程数,而且还有资源去添加更多线程的话,就去添加大于核心线程数的线程
- 如果资源不够,进行“拒绝的回调”,把数据返回给调用层
线程池关闭过程 shutdown
又是一个时序图较为复杂的调用,以下仅供参考。。。
直接来看代码:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查权限
checkShutdownAccess();
// 指定目标状态
advanceRunState(SHUTDOWN);
// 底层调用interrupt方法去终止空闲的线程
interruptIdleWorkers();
// 没啥用。。
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 把线程池改为终止状态
tryTerminate();
}
简述shutdown的过程:
- 改变状态,不再接受新的任务
- 每个空闲线程调用interrupt,强制终止线程(这里
不调用
stop,因为Thread的stop方法存在bug,jdk里面有说明) - 等待所有任务执行完毕,终止线程池
其他接口
shutdownNow
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 直接转变为STOP状态
advanceRunState(STOP);
interruptWorkers();
// 返回没有执行过的task
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
与shutdown不同的是,这个方法不再等待所有的任务执行完毕,而是把没有完成的任务列表返回。
awaitTermination
实战
shutdown
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()
);
executor.submit(() -> {
try {
Thread.sleep(10000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("调用了shutdown");
executor.shutdown();
System.out.println("执行完毕");
}
shutdown执行以后,需要等执行的线程自然结束以后,才会到最后的打印。
shutdownNow
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()
);
executor.submit(() -> {
try {
Thread.sleep(10000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("调用了shutdown");
executor.shutdownNow();
System.out.println("执行完毕");
}
执行shutdownNow后,由于直接对忙碌线程调用了interrupt方法,所以抛出以下异常,并且线程直接被打断
注:shutdownNow方法无法结束循环操作,实例如下
public class Main {
private static long i;
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println(i++);
}
}
});
Thread.sleep(1000);
executorService.shutdownNow();
}
}
一直在进行++的打印
awaitTermination
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()
);
executor.submit(() -> {
try {
Thread.sleep(2000);
System.out.println(String.format("%s, 执行完毕", Thread.currentThread().getName()));
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(executor.awaitTermination(3, TimeUnit.SECONDS));
System.out.println("查看shutdown状态:" + executor.isShutdown());
System.out.println("查看terminating状态:" + executor.isTerminating());
System.out.println("查看terminated状态:" + executor.isTerminated());
}
执行完毕后,出现以下打印,并且发现程序没有终止,线程池还在活跃。从现象可以证明await的过程只是等待而已,并不去操作线程池本身。
prestartAllCoreThreads,prestartCoreThread
提前启动核心线程,这样就免除了在使用时启动的时间。就不举例子了。
评价
线程池底层使用到了众多线程同步处理操作:
- Condition,用于同步
- Queue,用于传递数据
- Future,FutureTask等,用于获取返回结果和task控制
未完待续。。。。。。。。