1. 线程池状态
-
ThreadPoolExecutor 使用变量ctl定义为AtomicInteger,高 3 位来表示线程池的状态,低 29 位表示线程的任务数量。
-
任务转态
-
状态转换
RUNNING -> SHUTDOWN -> TIDYING -> TERMINATED
RUNNING -> STOP -> TIDYING -> TERMINATED
- 源代码
public class ThreadPoolExecutor extends AbstractExecutorService {
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~COUNT_MASK; }
private static int workerCountOf(int c) { return c & COUNT_MASK; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
...
}
2. 构造方法
- 代码
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- 参数说明
- 工作流程
- 创建线程池后等待提交过来的任务,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务。
- 当正在运行的线程数小于corePoolSize,直接创建线程执行该任务;当线程数达到 corePoolSize并且没有线程空闲,这时再加入执行的任务,新的任务会进入workQueue 队列排队,直到有空闲的线程。
- 如果队列为有界队列,当队列满时,会创建 maximumPoolSize - corePoolSize 数目的非核心线程来执行该任务;如果是无界队列则无需考虑创建非核心线程来执行,maximumPoolSize没有意义。
- 如果队列满了并且线程达到maximumPoolSize值,当再次有新任务时会执行拒绝策略。
- 超过corePoolSize 的非核心线程如果在一段时间处于空闲状态,这个时候由keepAliveTime 和 unit 来控制回收线程。
3. 线程池
Executors提供一些静态工厂方法,方便地创建预配置的线程池实例
3.1 newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
1. 特点
- 固定大小的线程池
- 核心线程数等于最大线程数
- 阻塞队列是无界的,没有限制
- 无需创建非核心线程数,空闲时间keepAliveTime为0
2. 说明
- 适用场景:任务数量已知且相对固定,任务执行时间较长(有效控制并发数量,防止短期任务占用大量资源)
3. 示例
@Slf4j
public class Demo01NewFixedThreadPool {
public static void main(String[] args) {
test01();
// test02();
}
public static void test01() {
// 线程池的线程数为2,循环三次调用线程
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 3; i++) {
int j = i;
executor.execute(() -> {
try {
TimeUnit.SECONDS.sleep(1);
log.info("{}", j);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
// 关闭线程池
executor.shutdown();
}
/**
* ThreadFactory -- 线程工厂 - 定义线程创建时的线程名
*/
public static void test02() {
// 线程池的线程数为2,循环三次调用线程
ExecutorService executor = Executors.newFixedThreadPool(2, new ThreadFactory() {
private final AtomicInteger num = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "pool-demo-t-" + num.getAndIncrement());
}
});
for (int i = 0; i < 3; i++) {
int j = i;
executor.execute(() -> {
try {
TimeUnit.SECONDS.sleep(1);
log.info("{}", j);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
// 关闭线程池
executor.shutdown();
}
}
3.2 newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
1. 特点
- 可缓存线程池
- 阻塞队列是无界的
- 核心线程数为0,最大线程数为Integer.MAX_VALUE,创建的线程都为非核心线程数
- keepAliveTime为60s,线程池中的线程在一段时间内(默认60秒)没有被使用将被终止移除
2. 说明
- 适合处理大量短期的(异步)任务,任务执行时间较短。
3. 示例
@Slf4j
public class Demo03NewCachedThreadPool {
public static void main(String[] args) {
test01();
}
public static void test01() {
// 每个任务都会创建一个新的线程执行
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
int j = i;
executor.submit(() ->{
log.info("{}", j);
try {
// 模拟任务执行时间
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
// 关闭线程
executor.shutdown();
}
}
3.3 newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
1. 特点
- 单线程的线程池
- 核心线程数等于最大线程数等于1,唯一的线程不会被释放
- 阻塞队列是无界的,没有限制
2. 说明
- 简单任务调用,顺序任务执行
3. 示例
/**
* newSingleThreadExecutor - 单个worker线程的Executor
* 线程数固定为1,当任务书多余1时,其余任务进入无界队列
*/
@Slf4j
public class Demo03NewSingleThreadExecutor {
public static void main(String[] args) {
test01();
}
/**
* 执行三个线程,当任务执行完才会执行下一个任务
* 其中有线程任务执行异常时, 会创建一个新的线程执行任务(注意看线程名字)
*/
public static void test01() {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(() -> {
// 模拟异常情况
log.debug("1");
int i = 1 / 0;
});
executorService.execute(() -> log.info("2"));
executorService.execute(() -> log.info("3"));
executorService.shutdown();
}
}
4. 任务调度线程池
4.1 ScheduledExecutorService
ScheduledExecutorService是 Java 中用于调度任务的接口,它是ExecutorService的子接口,提供了在给定的延迟后或定期执行任务的方法。通常用于替代传统的 java.util.Timer 和 java.util.TimerTask,因为它提供了更强大的功能和更好的灵活性。通过Executors工具类的静态方法来创建ScheduledExecutorService。
4.2 ScheduledExecutorService的几种任务调度方法
- schedule(Runnable command, long delay, TimeUnit unit)
在给定的延迟后执行一次任务。 - schedule(Callable callable, long delay, TimeUnit unit)
在给定的延迟后执行一次任务,并返回一个 Future 对象。 - scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
在给定的初始延迟后开始执行任务,并以固定的频率重复执行。 - scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
在给定的初始延迟后开始执行任务,并在每次执行完成后等待给定的延迟时间再执行下一次任务。
1. 示例
@Slf4j
public class Demo06ScheduledExecutorService {
public static void main(String[] args) {
// test01();
test02();
// test03();
}
/**
* 使用schedule执行两个任务,在1s之后执行
*/
public static void test01() {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
executor.schedule(() -> {
try {
log.info("task 1, time:{}", new Date());
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, 1000, TimeUnit.MILLISECONDS);
executor.schedule(() -> {
log.info("task 2, time:{}", new Date());
}, 1000, TimeUnit.MILLISECONDS);
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
/**
* 使用scheduleAtFixedRate执行任务
* 初始延迟为1秒,但是 任务执行时间 > 间隔时间,之后每隔2秒执行一次
*/
public static void test02() {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
log.info("begin...");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, 1000, 1000, TimeUnit.MILLISECONDS);
}
/**
* 使用scheduleWithFixedDelay执行任务
* 初始延迟为1秒,任务执行时间为3s > 间隔时间为2s,任务延迟时间为执行时间+间隔时间
*/
public static void test03() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
executorService.scheduleWithFixedDelay(() -> {
try {
log.info("begin...");
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, 1000, 2000, TimeUnit.MILLISECONDS);
}
}
2. 说明
资源管理:使用 ScheduledThreadPoolExecutor 时,需要注意正确关闭执行器,以释放系统资源。可以使用 shutdown() 方法来关闭执行器。
异常处理:如果任务抛出未捕获的异常,默认情况下会终止周期性任务的执行。可以通过设置 ThreadFactory 或 RejectedExecutionHandler 来处理异常。
任务间隔:scheduleAtFixedRate 和 scheduleWithFixedDelay 的区别在于前者是以固定的时间间隔执行任务,而后者是每次任务执行完后再延迟指定时间执行