彻底弄懂 Java 线程池原理

概述

这篇文章是我在阅读源码时整理的一些笔记,对源码的关键点进行了比较详细的注释,然后加上一些自己对线程池机制的理解。最终目的是要弄清楚下面这些问题:

  • 线程池有 execute() 和 submit() 方法,执行机制分别是什么?
  • 如何新建线程?
  • 任务如何执行?
  • 线程如何销毁?超时机制如何实现?

首先需要介绍一下线程池的两个重要成员:

ctl

AtomicInteger 类型。高3位存储线程池状态,低29位存储当前线程数量。workerCountOf(c) 返回当前线程数量。runStateOf(c) 返回当前线程池状态。 线程池有如下状态:

  • RUNNING:接收新任务,处理队列任务。
  • SHUTDOWN:不接收新任务,但处理队列任务。
  • STOP:不接收新任务,也不处理队列任务,并且中断所有处理中的任务。
  • TIDYING:所有任务都被终结,有效线程为0。会触发terminated()方法。
  • TERMINATED:当terminated()方法执行结束。

Worker

这个线程在线程池中的包装类。一个 Worker 代表一个线程。线程池用一个 HashSet 管理这些线程。

需要注意的是,Worker 本身并不区分核心线程和非核心线程,核心线程只是概念模型上的叫法,特性是依靠对线程数量的判断来实现的 Worker 特性如下:

  • 继承自 AQS,本身实现了一个最简单的不公平的不可重入锁。
  • 构造方法传入 Runnable,代表第一个执行的任务,可以为空。构造方法中新建一个线程。
  • 实现了 Runnable 接口,在新建线程时传入 this。因此线程启动时,会执行 Worker 本身的 run 方法。
  • run 方法调用了 ThreadPoolExecutor 的 runWorker 方法,负责实际执行任务。

submit() 方法的执行机制

submit 返回一个 Future 对象,我们可以调用其 get 方法获取任务执行的结果。代码很简单,就是将 Runnable 包装成 FutureTask 而已。可以看到,最终还是调用 Execute 方法:

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}
复制代码

FutureTask 的代码就不贴了,简述一下原理:

  • FutureTask 实现了 RunnableFuture 接口,RunnableFuture 继承自Runnable。执行任务时会调用 FutureTask 的 run 方法,run 方法中执行真正的任务代码,执行完后调用 set 方法设置结果。
  • 如果任务执行完毕,get 方法会直接返回结果,如果没有,get 方法会阻塞并等待结果。
  • set 方法中设置结果后会取消阻塞,使 get 方法返回结果。

execute() 方法的执行机制

这个机制大家应该都很熟了,再简述一遍:

  1. 工作线程数小于核心线程数时,直接新建核心线程执行任务;
  2. 大于核心线程数时,将任务添加进等待队列;
  3. 队列满时,创建非核心线程执行任务;
  4. <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值