From 989831f7be313eb86935809ee434910a00dba976 Mon Sep 17 00:00:00 2001 From: Kou Shuang Date: Mon, 18 Nov 2019 11:48:00 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E6=B1=A0=E5=A4=A7=E5=B0=8F?= =?UTF-8?q?=E7=A1=AE=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 - .../Multithread/java线程池学习总结.md | 36 ++++++++++++++++--- .../java/Multithread/线程池大小确定.md | 16 --------- 3 files changed, 32 insertions(+), 21 deletions(-) delete mode 100644 docs/java/Multithread/线程池大小确定.md diff --git a/README.md b/README.md index c678e592..c8c4d0e3 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,6 @@ * **[Java 并发进阶常见面试题总结](docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md)** * [并发容器总结](docs/java/Multithread/并发容器总结.md) * **[Java线程池学习总结](./docs/java/Multithread/java线程池学习总结.md)** -* [如何设置线程池大小?]() * [乐观锁与悲观锁](docs/essential-content-for-interview/面试必备之乐观锁与悲观锁.md) * [JUC 中的 Atomic 原子类总结](docs/java/Multithread/Atomic.md) * [AQS 原理以及 AQS 同步组件总结](docs/java/Multithread/AQS.md) diff --git a/docs/java/Multithread/java线程池学习总结.md b/docs/java/Multithread/java线程池学习总结.md index aecc4d52..91a7cbd4 100644 --- a/docs/java/Multithread/java线程池学习总结.md +++ b/docs/java/Multithread/java线程池学习总结.md @@ -37,8 +37,9 @@ - [6.1 简介](#61-简介) - [6.2 运行机制](#62-运行机制) - [6.3 ScheduledThreadPoolExecutor 执行周期任务的步骤](#63-scheduledthreadpoolexecutor-执行周期任务的步骤) -- [七 参考](#七-参考) -- [八 其他推荐阅读](#八-其他推荐阅读) +- [七 线程池大小确定](#七-线程池大小确定) +- [八 参考](#八-参考) +- [九 其他推荐阅读](#九-其他推荐阅读) @@ -740,14 +741,41 @@ Wed Nov 13 13:40:43 CST 2019::pool-1-thread-5 3. 线程 1 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间; 4. 线程 1 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。 -## 七 参考 +## 七 线程池大小确定 + +**线程池数量的确定一直是困扰着程序员的一个难题,大部分程序员在设定线程池大小的时候就是随心而定。我们并没有考虑过这样大小的配置是否会带来什么问题,我自己就是这大部分程序员中的一个代表。** + +由于笔主对如何确定线程池大小也没有什么实际经验,所以,这部分内容参考了网上很多文章/书籍。 + +**首先,可以肯定的一点是线程池大小设置过大或者过小都会有问题。合适的才是最好,貌似在 95 % 的场景下都是合适的。** + +如果阅读过我的上一篇关于线程池的文章的话,你一定知道: + +**如果我们设置的线程池数量太小的话,如果同一时间有大量任务/请求需要处理,可能会导致大量的请求/任务在任务队列中排队等待执行,甚至会出现任务队列满了之后任务/请求无法处理的情况,或者大量任务堆积在任务队列导致 OOM。这样很明显是有问题的! CPU 根本没有得到充分利用。** + +**但是,如果我们设置线程数量太大,大量线程可能会同时在争取 CPU 资源,这样会导致大量的上下文切换,从而增加线程的执行时间,影响了整体执行效率。** + +> 上下文切换: +> +> 多线程编程中一般线程的个数都大于 CPU 核心的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。概括来说就是:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。**任务从保存到再加载的过程就是一次上下文切换**。 +> +> 上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。所以,上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。 +> +> Linux 相比与其他操作系统(包括其他类 Unix 系统)有很多的优点,其中有一项就是,其上下文切换和模式切换的时间消耗非常少。 + +有一个简单并且适用面比较广的公式: + +- **CPU 密集型任务(N+1):** 这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。 +- **I/O 密集型任务(2N):** 这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。 + +## 八 参考 - 《Java 并发编程的艺术》 - [Java Scheduler ScheduledExecutorService ScheduledThreadPoolExecutor Example](https://www.journaldev.com/2340/java-scheduler-scheduledexecutorservice-scheduledthreadpoolexecutor-example "Java Scheduler ScheduledExecutorService ScheduledThreadPoolExecutor Example") - [java.util.concurrent.ScheduledThreadPoolExecutor Example](https://examples.javacodegeeks.com/core-java/util/concurrent/scheduledthreadpoolexecutor/java-util-concurrent-scheduledthreadpoolexecutor-example/ "java.util.concurrent.ScheduledThreadPoolExecutor Example") - [ThreadPoolExecutor – Java Thread Pool Example](https://www.journaldev.com/1069/threadpoolexecutor-java-thread-pool-example-executorservice "ThreadPoolExecutor – Java Thread Pool Example") -## 八 其他推荐阅读 +## 九 其他推荐阅读 - [Java 并发(三)线程池原理](https://www.cnblogs.com/warehouse/p/10720781.html "Java并发(三)线程池原理") - [如何优雅的使用和理解线程池](https://github.com/crossoverJie/JCSprout/blob/master/MD/ThreadPoolExecutor.md "如何优雅的使用和理解线程池") \ No newline at end of file diff --git a/docs/java/Multithread/线程池大小确定.md b/docs/java/Multithread/线程池大小确定.md deleted file mode 100644 index 8d0a69a8..00000000 --- a/docs/java/Multithread/线程池大小确定.md +++ /dev/null @@ -1,16 +0,0 @@ -**线程池数量的确定一直是困扰着程序员的一个难题,大部分程序员在设定线程池大小的时候就是随心而定。我们并没有考虑过这样大小的配置是否会带来什么问题,我自己就是这大部分程序员中的一个代表。** - -由于笔主对如何确定线程池大小也没有什么实际经验,所以,这部分内容参考了网上很多文章/书籍。 - -首先,可以肯定的一点是线程池大小设置过大或者过小都会有问题。如果阅读过我的上一篇关于线程池的文章的话,你一定知道: - -如果我们设置的线程池数量太小的话,如果同一时间有大量任务/请求需要处理,可能会导致大量的请求/任务在任务队列中排队等待执行,甚至会出现任务队列满了之后任务/请求无法处理的情况,或者大量任务堆积在任务队列导致 OOM。这样很明显是有问题的! CPU 根本没有得到充分利用。 - -但是,如果我们设置线程数量太大,大量线程可能会同时在争取 CPU 资源,这样会导致大量的上下文切换,从而增加线程的执行时间,影响了整体执行效率。 - -> 上下文切换: - -有一个简单并且适用面比较广的公式: - -- **CPU 密集型任务(N+1):**这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。 -- **I/O 密集型任务(2N):**这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。 \ No newline at end of file