Java线程池
spring 框架下有封装好的线程池,尽量优先选择 spring 提供的。
不推荐新手学习,因为学习门槛非常高,需要有一定的封装经验,否则会很难理解它的设计;
而且实战的机会非常少,学完可能几年内都用不上。
不必担心面试提问,因为很少会直接使用,面试你的人大概率也是没用过的,用过也只能说明他可能在工作在摸鱼。
Executors
Executors提供四种线程池,分别为:
(注意名字带s,这是个工具类,不推荐使用Executors,应当显式创建线程池)
- newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程。如果任务执行结束,线程会被其他任务继续使用,而不用每次都新建线程。 - newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。 - newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行,他有几个特殊的方法:
schedule()方法更注重保持间隔时间的稳定;
scheduleAtFixedRate()方法更注重保持执行频率的稳定,以固定的频率来执行某项计划(任务);
scheduleWithFixedDelay(),相对固定的延迟后,执行某项计划。
- newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
ThreadPoolExecutor
Executor和ThreadPoolExecutor的关系
1、Executor是一个顶层接口,在它里面只声明了一个方法 execute(Runnable),就是用来执行任务的;
2、ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
3、抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;
4、而ThreadPoolExecutor继承自AbstractExecutorService。
ThreadPoolExecutor构造方法参数
corePoolSize - 池中所保存的线程数,包括空闲线程。
maximumPoolSize - 池中允许的最大线程数。
keepAliveTime - 线程池中超过corePoolSize数目时,空闲线程的最大存活时间
unit - keepAliveTime 参数的时间单位。
workQueue - 执行前用于保持任务的队列。此队列仅保持由execute方法提交的 Runnable 任务。
threadFactory - 执行程序创建新线程时使用的工厂。
handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
ThreadPoolExecutor构造参数的作用
一句话概括:小于maximumPoolSize就能新建线程,大于corePoolSize就放入WorkQueue缓冲队列;
超过最大处理能力就交给Handler处理,超过最大存活时间线程池就会关闭(超出corePoolSize部分,也可自定义),线程池分类不同,具体情况略有不同:
1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
6.当设置allowCoreThreadTimeOut(true)时,线程池中小于corePoolSize的线程空闲时间达到keepAliveTime也将关闭
unit可选的参数(构造方法 keepAliveTime 参数)
keepAliveTime参数的时间单位,有以下可选:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
阻塞队列(构造方法 workQueue 参数)
要处理的任务太多了,线程池处理不过来,那就先把任务缓存起来。
workQueue指的是阻塞队列,用来存储等待执行的任务,一般来说,这里的阻塞队列有以下几种选择:
PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序.
ArrayBlockingQueue: 规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的.
LinkedBlockingQueue: 基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
拒绝策略(构造方法 handler 参数)
要处理的任务还是太多,该怎么怎么办?表示当拒绝处理任务时的策略,有以下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
较常用的方法
线程执行
- execute() - 在将来某个时间执行给定任务。可以在新线程中或者在现有池线程中执行该任务。如果无法将任务提交执行,或者因为此执行程序已关闭,或者因为已达到其容量,则该任务由当前
RejectedExecutionHandler 处理。 - submit() - 提交一个事务(参数可以是:runnable对象,回调接口,或者是与泛型相同的对象),它能够返回任务执行的结果,
线程关闭
- shutdown() - 按过去执行已提交任务的顺序发起一个有序的关闭,但是不接受新任务。如果已经关闭,则调用没有其他作用。
- shutdownNow() - 尝试停止所有的活动执行任务、暂停等待任务的处理,并返回等待执行的任务列表。在从此方法返回的任务队列中排空(移除)这些任务。
并不保证能够停止正在处理的活动执行任务,但是会尽力尝试。 此实现通过 Thread.interrupt()
取消任务,所以无法响应中断的任何任务可能永远无法终止。
其它
- purge() - 尝试从工作队列移除所有已取消的Future任务。此方法可用作存储回收操作,它对功能没有任何影响。取消的任务不会再次执行,但是它们可能在工作队列中累积,直到worker线程主动将其移除。调用此方法将试图立即移除它们。但是,如果出现其他线程的干预,那么此方法移除任务将失败。
- getQueue() - 返回此执行程序使用的任务队列。对任务队列的访问主要用于调试和监控。此队列可能正处于活动使用状态中。获取任务队列不妨碍已加入队列的任务的执行。
- remove() - 从执行程序的内部队列中移除此任务(如果存在),从而如果尚未开始,则其不再运行。
几个供子类重写的方法
- beforeExecute() - 在执行给定线程中的给定 Runnable 之前调用的方法。此方法由将执行任务 r 的线程 t 调用,并且可用于重新初始化 ThreadLocals 或者执行日志记录。
此实现不执行任何操作,但可在子类中定制。注:为了正确嵌套多个重写操作,此方法结束时,子类通常应该调用
super.beforeExecute。 - afterExecute() - 基于完成执行给定 Runnable 所调用的方法。此方法由执行任务的线程调用。如果非 null,则 Throwable 是导致执行突然终止的未捕获 RuntimeException 或 Error。
注:当操作显示地或者通过submit 之类的方法包含在任务内时(如FutureTask),这些任务对象捕获和维护计算异常,因此它们不会导致突然终止,内部异常不会 传递给此方法。
此实现不执行任何操作,但可在子类中定制。
注:为了正确嵌套多个重写操作,此方法开始时,子类通常应该调用
super.afterExecute。 - terminated() - 当 Executor 已经终止时调用的方法。默认实现不执行任何操作。
注:为了正确嵌套多个重写操作,子类通常应该在此方法中调用super.afterExecute。 - finalize() - protected void finalize()当不再引用此执行程序时,调用 shutdown。
ThreadPoolExecutor类中其他的一些比较重要成员变量
private final BlockingQueue[Runnable] workQueue;
//任务缓存队列,用来存放等待执行的任务
private final ReentrantLock mainLock = new ReentrantLock();
//线程池的主要状态锁,对线程池状态(比如线程池大小、runState等)的改变都要使用这个锁
private final HashSet workers = new HashSet);
//用来存放工作集
private volatile long keepAliveTime; //线程存货时间
private volatile boolean allowCoreThreadTimeOut;
//是否允许为核心线程设置存活时间
private volatile int corePoolSize;
//核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)
private volatile int maximumPoolSize; //线程池最大能容忍的线程数
private volatile int poolSize; //线程池中当前的线程数
private volatile RejectedExecutionHandler handler; //任务拒绝策略
private volatile ThreadFactory threadFactory; //线程工厂,用来创建线程
private int largestPoolSize; //用来记录线程池中曾经出现过的最大线程数
private long completedTaskCount; //用来记录已经执行完毕的任务个数
一些参考经验:
- 用ThreadPoolExecutor自定义线程池,看线程是的用途,如果任务量不大,可以用无界队列,如果任务量非常大,要用有界队列,防止OOM1.
- 如果任务量很大,还要求每个任务都处理成功,要对提交的任务进行阻塞提交,重写拒绝机制,改为阻塞提交。保证不抛弃一个任务
- 最大线程数一般设为2N+1最好,N是CPU核数
- 核心线程数,看应用,如果是任务,一天跑一次,设置为0,合适,因为跑完就停掉了,如果是常用线程池,看任务量,是保留一个核心还是几个核心线程数
- 如果要获取任务执行结果,用CompletionService,但是注意,获取任务的结果的要重新开一个线程获取,如果在主线程获取,就要等任务都提交后才获取,就会阻塞大量任务结果,队列过大OOM,所以最好异步开个线程获取结果
相关代码
一种个人比较喜欢的关闭线程的方式
class MyRunnable implements Runnable{
private boolean stop;
private Thread thread;
public MyRunnable() {
stop = false;
thread = Thread.currentThread();
}
@Override
public void run() {
int count = 0;
while (!stop) {
try {
System.out.println(count++);
Thread.sleep(200);
} catch (InterruptedException e) {
this.thread.interrupt();
}
}
}
public void stopThread() {
this.stop = !stop;
}
}
public class SafelyStop {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myRunnable.stopThread();
}
}
工具代码
在实际项目开发过程中,没有太多实用价值,但是教学的时候,很方便实用,能快速构造起各种队列。
class ThreadUtil {
private static ThreadPoolExecutor threadPool;
private static ExecutorService cachedThreadPool;
private static ExecutorService fixedThreadPool;
private static ScheduledExecutorService scheduledThreadPool;
private static ExecutorService singleThreadExecutor;
/**
* 个人比较喜欢的一种单例模式写法,不知道有没有人这么写
*
* @return 线程池
*/
public static ThreadPoolExecutor getThreadpool() {
if (threadPool == null)
createThreadPool();
return threadPool;
}
public static ExecutorService getCachedThreadPool() {
if (cachedThreadPool == null)
crateCachedThreadPool();
return cachedThreadPool;
}
public static ExecutorService getFixedThreadPool() {
if (fixedThreadPool == null)
crateFixedThreadPool();
return fixedThreadPool;
}
public static ScheduledExecutorService getScheduledThreadPool() {
if (scheduledThreadPool == null)
crateScheduledThreadPool();
return scheduledThreadPool;
}
public static ExecutorService getSingleThreadExecutor() {
if (singleThreadExecutor == null)
crateSingleThreadExecutor();
return singleThreadExecutor;
}
/**
* 单例线程
*/
private static void crateSingleThreadExecutor() {
if (singleThreadExecutor == null)
singleThreadExecutor = Executors.newSingleThreadExecutor(new CustomThreadFactory());
}
/**
* 创建一个定长线程池,支持定时及周期性任务执行。
*/
private static void crateScheduledThreadPool() {
if (scheduledThreadPool == null)
scheduledThreadPool = Executors.newScheduledThreadPool(3, new CustomThreadFactory());
}
/**
* 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
*/
private static void crateFixedThreadPool() {
if (fixedThreadPool == null)
fixedThreadPool = Executors.newFixedThreadPool(4, new CustomThreadFactory());
}
/**
* 带缓存的队列,如果有新的事务,自动使用之前的线程,线程会自动回收
*/
private static void crateCachedThreadPool() {
if (cachedThreadPool == null)
cachedThreadPool = Executors.newCachedThreadPool(new CustomThreadFactory());
}
/**
* 自定义线程池
*/
private synchronized static void createThreadPool() {
if (threadPool == null)
threadPool = new ThreadPoolExecutor(
3, // 线程数量
5, // 线程池最大值
2, // 最大存活时间
TimeUnit.SECONDS, // 按照秒计算
new ArrayBlockingQueue<Runnable>(5), // 缓冲池(阻塞队列)
new CustomThreadFactory(), // 线程工厂
new CustomRejectedExecutionHandler()// 超出最大处理能力处理机制(拒绝策略)
);
}
/**
* 关闭所有的线程
*/
public static void destory() {
if (!threadPool.isShutdown()) {
threadPool.shutdownNow();
}
}
/**
* 自定义线程工厂,这里给每一个线程取了一个名字
*
* @author ChenSS
*
*/
private static class CustomThreadFactory implements ThreadFactory {
private AtomicInteger count = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
String threadName = ThreadFactory.class.getSimpleName() + count.addAndGet(1);
thread.setName(threadName);
return thread;
}
}
/**
* 自定义处理策略
*
* @author ChenSS
*
*/
private static class CustomRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable runnable, ThreadPoolExecutor executor) {
try {
// 超出最大线程池极限的时候的处理机制,这里也可以直接拒绝、或者说抛出异常,而这里是等待
executor.getQueue().put(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试函数
线程池有用,但是实践机会很少,很容易忘记,下面这个代码,可以用于测试各种线程池的功能。
package com;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Test {
/**
* 容量大小固定的线程池:因为线程池大小限制为4,所以一次输出4个数字,并且会发现线程被复用了
*/
public static void testFixedThreadPool() {
for (int i = 0; i < 10; i++) {
final int index = i;
ThreadUtil.getFixedThreadPool().execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + ":" + index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
/**
* 带延迟效果的线程池:线程延迟了3秒执行,容量大小,而且线程只新建了3个
*/
public static void testScheduledThreadPool1() {
for (int i = 0; i < 10; i++) {
ThreadUtil.getScheduledThreadPool().schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + "delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
}
}
/**
* 带延迟效果的线程池:线程延迟了1秒执行,之后每3秒执行一次,而且线程是3个中的随机一个,以固定的周期执行
*/
public static void testScheduledThreadPool2() {
ThreadUtil.getScheduledThreadPool().scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + ":" + "delay 3 seconds");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 1, 2, TimeUnit.SECONDS);
}
/**
* 带延迟效果的线程池:线程延迟了1秒执行,之后每3秒执行一次,而且线程是3个中的随机一个,执行完任务,以固定的周期进行延迟
*/
public static void testScheduledThreadPool3() {
ThreadUtil.getScheduledThreadPool().scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + ":" + "delay 3 seconds");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 1, 2, TimeUnit.SECONDS);
}
/**
* 可自动回收线程的线程池:Java默认60秒自动回收,测试回收功能
*/
public static void testCachedThreadPool() {
for (int i = 0; i < 10; i++) {
final int index = i;
ThreadUtil.getCachedThreadPool().execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + index);
}
});
try {
Thread.sleep(70000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 这是一个单例线程,可以看到:所有的事务使用同一个线程
*/
public static void testSingleThreadExecutor() {
for (int i = 0; i < 10; i++) {
final int index = i;
ThreadUtil.getSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + index);
}
});
}
}
/**
* 自定义线程池测试
*/
public static void testThreadPoolExecutor() {
for (int i = 1; i < 20; i++) {
System.out.println("提交第" + i + "个任务!");
ThreadUtil.getThreadpool().execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "running=====");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "finish=====");
}
});
}
}
public static void main(String[] args) throws Exception {
// testFixedThreadPool();
// testCachedThreadPool();
// testScheduledThreadPool2();
testSingleThreadExecutor();
}
}
Java文档中的案例:添加了简单的暂停/恢复功能的子类
/**
* 下面是一个添加了简单的暂停/恢复功能的子类:
*
* @author ChenSS
*
*/
class PausableThreadPoolExecutor extends ThreadPoolExecutor {
private boolean isPaused;
private ReentrantLock pauseLock = new ReentrantLock();
private Condition unpaused = pauseLock.newCondition();
public PausableThreadPoolExecutor(……){
//构造方法,需要哪个自己选择super(......)
}
protected void beforeExecute(Thread t, Runnable r) {
pauseLock.lock();
try {
while (isPaused)
unpaused.await();
} catch (InterruptedException ie) {
t.interrupt();
} finally {
pauseLock.unlock();
}
super.beforeExecute(t, r);
}
public void pause() {
pauseLock.lock();
try {
isPaused = true;
} finally {
pauseLock.unlock();
}
}
public void resume() {
pauseLock.lock();
try {
isPaused = false;
unpaused.signalAll();
} finally {
pauseLock.unlock();
}
}
/**
* 当不再引用此执行程序时,调用 shutdown。
*/
@Override
protected void finalize() {
//关闭前做什么?功能自己扩展……
super.finalize();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY