Skip to content

Commit a5810c0

Browse files
authored
Merge pull request #2398 from MrlittleTim/dev
优化 java-concurrent-questions-03 部分内容,优化具体内容见正文
2 parents aea76e2 + 7a9e532 commit a5810c0

File tree

1 file changed

+51
-40
lines changed

1 file changed

+51
-40
lines changed

Diff for: docs/java/concurrent/java-concurrent-questions-03.md

+51-40
Original file line numberDiff line numberDiff line change
@@ -375,45 +375,55 @@ public static class CallerRunsPolicy implements RejectedExecutionHandler {
375375
这里简单举一个例子,该线程池限定了最大线程数为 2,还阻塞队列大小为 1(这意味着第 4 个任务就会走到拒绝策略),`ThreadUtil`为 Hutool 提供的工具类:
376376

377377
```java
378-
Logger log = LoggerFactory.getLogger(ThreadPoolTest.class);
379-
// 创建一个线程池,核心线程数为1,最大线程数为2
380-
// 当线程数大于核心线程数时,多余的空闲线程存活的最长时间为60秒,
381-
// 任务队列为容量为1的ArrayBlockingQueue,饱和策略为CallerRunsPolicy。
382-
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,
383-
2,
384-
60,
385-
TimeUnit.SECONDS,
386-
new ArrayBlockingQueue<>(1),
387-
new ThreadPoolExecutor.CallerRunsPolicy());
388-
389-
// 提交第一个任务,由核心线程执行
390-
threadPoolExecutor.execute(() -> {
391-
log.info("核心线程执行第一个任务");
392-
ThreadUtil.sleep(1, TimeUnit.MINUTES);
393-
});
394-
395-
// 提交第二个任务,由于核心线程被占用,任务将进入队列等待
396-
threadPoolExecutor.execute(() -> {
397-
log.info("非核心线程处理入队的第二个任务");
398-
ThreadUtil.sleep(1, TimeUnit.MINUTES);
399-
});
400-
401-
// 提交第三个任务,由于核心线程被占用且队列已满,创建非核心线程处理
402-
threadPoolExecutor.execute(() -> {
403-
log.info("非核心线程处理第三个任务");
404-
ThreadUtil.sleep(1, TimeUnit.MINUTES);
405-
});
406-
407-
// 提交第四个任务,由于核心线程和非核心线程都被占用,队列也满了,根据CallerRunsPolicy策略,任务将由提交任务的线程(即主线程)来执行
408-
threadPoolExecutor.execute(() -> {
409-
log.info("主线程处理第四个任务");
410-
ThreadUtil.sleep(2, TimeUnit.MINUTES);
411-
});
412-
413-
// 提交第五个任务,主线程被第四个任务卡住,该任务必须等到主线程执行完才能提交
414-
threadPoolExecutor.execute(() -> {
415-
log.info("核心线程执行第五个任务");
416-
});
378+
public class ThreadPoolTest {
379+
380+
private static final Logger log = LoggerFactory.getLogger(ThreadPoolTest.class);
381+
382+
public static void main(String[] args) {
383+
// 创建一个线程池,核心线程数为1,最大线程数为2
384+
// 当线程数大于核心线程数时,多余的空闲线程存活的最长时间为60秒,
385+
// 任务队列为容量为1的ArrayBlockingQueue,饱和策略为CallerRunsPolicy。
386+
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,
387+
2,
388+
60,
389+
TimeUnit.SECONDS,
390+
new ArrayBlockingQueue<>(1),
391+
new ThreadPoolExecutor.CallerRunsPolicy());
392+
393+
// 提交第一个任务,由核心线程执行
394+
threadPoolExecutor.execute(() -> {
395+
log.info("核心线程执行第一个任务");
396+
ThreadUtil.sleep(1, TimeUnit.MINUTES);
397+
});
398+
399+
// 提交第二个任务,由于核心线程被占用,任务将进入队列等待
400+
threadPoolExecutor.execute(() -> {
401+
log.info("非核心线程处理入队的第二个任务");
402+
ThreadUtil.sleep(1, TimeUnit.MINUTES);
403+
});
404+
405+
// 提交第三个任务,由于核心线程被占用且队列已满,创建非核心线程处理
406+
threadPoolExecutor.execute(() -> {
407+
log.info("非核心线程处理第三个任务");
408+
ThreadUtil.sleep(1, TimeUnit.MINUTES);
409+
});
410+
411+
// 提交第四个任务,由于核心线程和非核心线程都被占用,队列也满了,根据CallerRunsPolicy策略,任务将由提交任务的线程(即主线程)来执行
412+
threadPoolExecutor.execute(() -> {
413+
log.info("主线程处理第四个任务");
414+
ThreadUtil.sleep(2, TimeUnit.MINUTES);
415+
});
416+
417+
// 提交第五个任务,主线程被第四个任务卡住,该任务必须等到主线程执行完才能提交
418+
threadPoolExecutor.execute(() -> {
419+
log.info("核心线程执行第五个任务");
420+
});
421+
422+
// 关闭线程池
423+
threadPoolExecutor.shutdown();
424+
}
425+
}
426+
417427
```
418428

419429
输出:
@@ -496,7 +506,8 @@ new RejectedExecutionHandler() {
496506

497507
- 容量为 `Integer.MAX_VALUE``LinkedBlockingQueue`(无界队列):`FixedThreadPool``SingleThreadExector``FixedThreadPool`最多只能创建核心线程数的线程(核心线程数和最大线程数相等),`SingleThreadExector`只能创建一个线程(核心线程数和最大线程数都是 1),二者的任务队列永远不会被放满。
498508
- `SynchronousQueue`(同步队列):`CachedThreadPool``SynchronousQueue` 没有容量,不存储元素,目的是保证对于提交的任务,如果有空闲线程,则使用空闲线程来处理;否则新建一个线程来处理任务。也就是说,`CachedThreadPool` 的最大线程数是 `Integer.MAX_VALUE` ,可以理解为线程数是可以无限扩展的,可能会创建大量线程,从而导致 OOM。
499-
- `DelayedWorkQueue`(延迟阻塞队列):`ScheduledThreadPool``SingleThreadScheduledExecutor``DelayedWorkQueue` 的内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用的是“堆”的数据结构,可以保证每次出队的任务都是当前队列中执行时间最靠前的。`DelayedWorkQueue` 添加元素满了之后会自动扩容原来容量的 1/2,即永远不会阻塞,最大扩容可达 `Integer.MAX_VALUE`,所以最多只能创建核心线程数的线程。
509+
- `DelayedWorkQueue`(延迟队列):`ScheduledThreadPool``SingleThreadScheduledExecutor``DelayedWorkQueue` 的内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用的是“堆”的数据结构,可以保证每次出队的任务都是当前队列中执行时间最靠前的。`DelayedWorkQueue` 添加元素满了之后会自动扩容,增加原来容量的 50%,即永远不会阻塞,最大扩容可达 `Integer.MAX_VALUE`,所以最多只能创建核心线程数的线程。
510+
- `ArrayBlockingQueue`(有界阻塞队列):底层由数组实现,容量一旦创建,就不能修改。
500511

501512
### 线程池处理任务的流程了解吗?
502513

0 commit comments

Comments
 (0)