线程池原理&常用四大线程池及七大参数

本文详细介绍了Java中四种常用的线程池:可缓存线程池、固定线程池、单线程执行器和定时线程池的工作原理及应用场景。同时深入探讨了线程池的七大核心参数及其工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

最近在面试,关于一些多线程的内容,细致的部分很容易就想不起来具体应该怎么回答。翻开之前的笔记本,感觉就在眼前,知识不去实践,就像是别人的。尤其是程序猿这行,源源不断的新技术,就不要做捡了芝麻丢了西瓜的人。
总结在博客上,也能再温习一下。用不用的先门清~

常用的四种线程池

newCachedThreadPool——可缓存线程池

描述:

  • 如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

特点

  • 线程的创建数量几乎没有限制。这样可灵活的往线程池中添加线程。
  • 如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程
  • 要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class executor {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            try {
                Thread.sleep(index*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            pool.execute(new Runnable() {
                public void run() {
                    System.out.println(index);
                }
            });
        }
    }
}

在这里插入图片描述

newFixedThreadPool————指定线程数量

描述:

  • 创建一个指定工作线程数量的线程池。
  • 每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

优点:

  • 提高程序效率和节省创建线程时所耗的开销

缺点:

  • 线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class fixedThreadPool {
    public volatile int i;
    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);  
        for (int i = 0; i < 10; i++) {
            final int index = i;
            fixedThreadPool.execute(new Runnable() {
                public void run() {
                    try {
                        System.out.println(index);
                        Thread.sleep(8000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

运行结果:两个线程一起执行,隔8s切换
在这里插入图片描述

newSingleThreadExecutor————单线程的Executor

描述:

  • 只创建唯一的线程来执行任务。保证任务按照指定顺序(FIFO,LIFO,优先级)执行。
  • 如果这个线程异常结束,会有另一个取代它
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class singleThreadExecutor {
    public static void main(String[] args) {
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            singleThreadExecutor.execute(new Runnable() {
                public void run() {
                    try {
                        System.out.println(index);
                        Thread.sleep(8000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}


运行:单线程运行
在这里插入图片描述

newScheduleThreadPool——定时线程池

描述:

  • 支持定时及周期性任务执行。

栗子:

  • 延迟3s执行示例代码
import java.sql.Time;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class scheduledThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
       scheduledThreadPool.schedule(new Runnable() {
           public void run() {
               System.out.println("延迟5秒执行");
           }
       },5, TimeUnit.SECONDS);
    }
}

在这里插入图片描述

栗子:

  • 延迟2秒后每5秒执行一次,定期执行示例代码
import java.sql.Time;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class scheduledThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
       scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
           public void run() {
               System.out.println("延迟5秒执行");
           }
       },2,5,TimeUnit.SECONDS);
    }
}

运行:
在这里插入图片描述

线程池七大参数

corePoolSize——核心线程最大数

maximumPoolSize——线程池最大线程数

keepAliveTime——空闲线程存活时间。

  • 当一个非核心线程被创建,使用完归还给线程池
  • 一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由
    keepAliveTime来设定

unit——空闲线程存活时间单位

  • 这是keepAliveTime的计量单位

workQueue——等待队列

  • 当线程池满了,线程就会放入这个队列中。任务调度再取出

jdk提供四种工作队列

  1. ArrayBlockingQueue——基于数组的阻塞队列,按FIFO,新任务放队尾
    • 有界的数组可以防止资源耗尽问题。
    • 当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。
    • 如果队列已经是满的,则创建一个新线程,
    • 如果线程数量已经达到maxPoolSize,则会执行拒绝策略
  2. LinkedBlockingQuene——基于链表的无界阻塞队列,按照FIFO排序
    • 其实最大容量为Interger.MAX
    • 由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,
    • 因此使用该工作队列时,参数maxPoolSize其实是不起作用的
  3. SynchronousQuene——不缓存任务的阻塞队列
    • 生产者放入一个任务必须等到消费者取出这个任务。
    • 也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,
    • 如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略
  4. PriorityBlockingQueue——具有优先级的无界阻塞队列
    • 优先级通过参数Comparator实现。

threadFactory

  • 创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等

handler——拒绝策略

当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,就用到拒绝策略
jdk中提供了4中拒绝策略

  1. CallerRunsPolicy——主线程自己执行该任务
    • 该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,否则直接抛弃任务。
  2. AbortPolicy——抛出异常
    • 该策略下,直接丢弃任务,并抛出RejectedExecutionException异常
  3. DiscardPolicy——直接丢弃
    • 该策略下,直接丢弃任务,什么都不做
  4. DiscardOldestPolicy——早删晚进
    • 该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

线程池工作流程

  • 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
  • 调用 execute() 方法添加一个任务时,线程池会做如下判断:
    • 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
    • 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
    • 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
    • 如果队列满了,而且正在运行的线程数量等于 maximumPoolSize,那么线程池会抛出异常 RejectExecutionException。
  • 当一个线程完成任务时,它会从队列中取下一个任务来执行。
  • 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

在这里插入图片描述

&lt;think&gt;嗯,用户之前让我介绍了JUC并发工具包,现在又问线程池,显然想更深入了解线程池的部分。首先,我需要回顾之前提到的线程池内容,确保这次回答更详细全面。 用户可能对线程池的基本概念有一定了解,但需要深入原理参数配置、使用场景以及注意事项。他们可能在实际开发中遇到了性能问题,或者想优化现有的线程池配置。 我需要分结构讲解,比如核心参数、工作流程、常见类型、配置建议和注意事项。要避免使用过多专业术语,用更易懂的例子和场景说明。例如,解释corePoolSize和maxPoolSize时,可以用银行柜台的例子,让读者更容易理解。 另外,用户可能对如何选择线程池类型和配置参数有疑问,比如为什么推荐自定义ThreadPoolExecutor而不是Executors的工厂方法。需要强调资源耗尽的风险,比如OOM,并举出newFixedThreadPool和newCachedThreadPool的问题。 还要提到线程池的工作流程,任务队列的不同类型及其影响,比如LinkedBlockingQueue和SynchronousQueue的区别。可能用户在处理高并发任务时,对任务排队和拒绝策略不太清楚,需要详细说明四种拒绝策略及其适用场景。 在注意事项部分,需要提醒线程池的资源隔离、异常处理以及监控的重要性。特别是异常处理,如果不当可能导致任务静默失败,影响排查。可以给出使用UncaughtExceptionHandler或者Future.get()捕获异常的方法。 最后,总结部分要简明扼要,强调线程池的优势和正确使用的重要性,帮助用户形成系统化的理解。确保回答结构清晰,层次分明,让用户能逐步掌握线程池核心知识点。&lt;/think&gt;### 线程池深度解析:Java并发编程的发动机 线程池是 JUC 中资源管理的核心工具,通过复用线程降低创建/销毁开销,提升系统吞吐量。以下从底层原理到实践场景全面剖析: --- #### 一、线程池核心参数(以 `ThreadPoolExecutor` 为例) ```java new ThreadPoolExecutor( int corePoolSize, // 核心线程数(常驻线程) int maximumPoolSize, // 最大线程数(应急线程) long keepAliveTime, // 非核心线程空闲存活时间 TimeUnit unit, // 时间单位 BlockingQueue&lt;Runnable&gt; workQueue, // 任务队列 ThreadFactory threadFactory, // 线程创建工厂 RejectedExecutionHandler handler // 拒绝策略 ) ``` **参数比喻**: - 核心线程 = 银行固定柜台 - 最大线程 = 临时加开的流动柜台 - 任务队列 = 客户等候区 - 拒绝策略 = 客满时的处理方式 --- #### 二、线程池工作流程(关键执行逻辑) 1. 提交任务时优先使用核心线程 2. 核心线程满 &rarr; 任务进入队列 3. 队列满 &rarr; 创建非核心线程(不超过 maximumPoolSize) 4. 线程和队列均满 &rarr; 触发拒绝策略 **流程图**: ``` 新任务提交 &rarr; 核心线程是否满? ├─ 否 &rarr; 创建核心线程执行 └─ 是 &rarr; 队列是否满? ├─ 否 &rarr; 加入队列等待 └─ 是 &rarr; 能否创建新线程? ├─ 能 &rarr; 创建临时线程执行 └─ 不能 &rarr; 执行拒绝策略 ``` --- #### 三、四大拒绝策略对比 | 策略类 | 行为 | 适用场景 | |---------------------------------|--------------------------------------|-----------------------| | `AbortPolicy`(默认) | 抛出 `RejectedExecutionException` | 严格要求任务完整性 | | `CallerRunsPolicy` | 由提交任务的线程直接执行 | 流量削峰,降低提交速度 | | `DiscardPolicy` | 静默丢弃新任务 | 允许丢任务(如日志监控)| | `DiscardOldestPolicy` | 丢弃队列最旧任务,重试提交新任务 | 优先处理最新任务 | --- #### 四、常见线程池类型(通过 `Executors` 创建) 1. **固定线程池** ```java ExecutorService fixedPool = Executors.newFixedThreadPool(5); ``` - 特点:corePoolSize = maximumPoolSize,队列无界 - 风险:任务堆积可能导致 OOM 2. **缓存线程池** ```java ExecutorService cachedPool = Executors.newCachedThreadPool(); ``` - 特点:corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE,队列为 `SynchronousQueue` - 风险:线程数量失控 3. **单线程池** ```java ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); ``` - 特点:保证任务顺序执行,队列无界 - 风险:同固定线程池 4. **定时线程池** ```java ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3); scheduledPool.scheduleAtFixedRate(task, initialDelay, period, unit); ``` - 支持定时/周期性任务 --- #### 五、线程池配置黄金法则 1. **CPU 密集型任务** ```math corePoolSize = CPU核心数 + 1 ``` (如 8 核 CPU &rarr; 9 线程) 2. **IO 密集型任务** ```math corePoolSize = CPU核心&times; 2 ``` (如 8 核 CPU &rarr; 16 线程) 3. **混合型任务** - 拆分为 CPU 密集和 IO 密集两部分 - 分别使用不同线程池 --- #### 六、生产环境最佳实践 1. **禁止使用 `Executors` 创建线程池** - 推荐手动配置 `ThreadPoolExecutor` 参数 ```java // 标准安全配置示例 new ThreadPoolExecutor( Runtime.getRuntime().availableProcessors(), // 核心线程数 Runtime.getRuntime().availableProcessors() * 2, // 最大线程数 60, TimeUnit.SECONDS, new ArrayBlockingQueue&lt;&gt;(1000), // 有界队列 new CustomThreadFactory(), // 自定义线程命名 new ThreadPoolExecutor.CallerRunsPolicy() // 降级策略 ); ``` 2. **监控关键指标** ```java ThreadPoolExecutor pool = (ThreadPoolExecutor) executor; System.out.println(&quot;活跃线程数:&quot; + pool.getActiveCount()); System.out.println(&quot;已完成任务数:&quot; + pool.getCompletedTaskCount()); System.out.println(&quot;队列大小:&quot; + pool.getQueue().size()); ``` 3. **优雅关闭** ```java executor.shutdown(); // 停止接收新任务 if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { executor.shutdownNow(); // 强制终止 } ``` --- #### 七、常见问题与解决方案 1. **线程池打满导致服务不可用** - 增加队列容量 - 优化任务处理速度 - 使用熔断机制 2. **线程上下文切换开销大** - 降低线程数量 - 使用协程(需第三方库,如 Quasar) 3. **任务执行异常丢失** - 在任务内加 try-catch - 使用 `Future` 获取异常 ```java Future&lt;?&gt; future = executor.submit(task); try { future.get(); } catch (ExecutionException e) { Throwable cause = e.getCause(); // 处理异常 } ``` --- ### 总结:线程池的三层认知 1. **基础层**:理解核心参数与工作流程 2. **实践层**:根据业务特点选择配置策略 3. **高级层**:监控调优与异常处理体系 **特别提示**:在高并发场景下,建议为不同业务类型使用独立线程池,避免相互影响(如订单处理与日志记录分开)。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值