涉及到线程池之前,我们需要知道创建线程的几种方式,在线程的基础之上,我们进一步学习线程池。
博主魔改博客链接位置:欢迎各位大大们来观看。
博主魔改博客本文地址
线程池
线程池是什么?
线程池是一种池化技术,使用池化技术管理和使用线程的一种机制。
池化技术:在利用资源之前,我们需要提前准备一些资源,在需要时候重复使用提前准备的资源。
池化技术(Pooling)是一种常用的优化技术,它的目的是通过重复使用已经创建的对象或者资源,来避免频繁的创建和销毁对象或者资源所带来的开销。通过池化技术,可以减少创建和销毁对象的开销,从而提高程序的性能和稳定性。在计算机领域中,池化技术通常用于以下几个方面:
- 数据库连接池:通过重复使用已经创建的数据库连接,避免频繁地创建和销毁数据库连接所带来的开销。
- 线程池:通过重复使用已经创建的线程,避免频繁地创建和销毁线程所带来的开销。
- 对象池:通过重复使用已经创建的对象,避免频繁地创建和销毁对象所带来的开销。
- 内存池:通过重复使用已经分配的内存,避免频繁地分配和释放内存所带来的开销。
在实际应用中,池化技术可以帮助我们提高程序的性能和稳定性,因此在设计和开发程序时,可以考虑采用池化技术来优化程序的性能。
线程池的使用方式
- 通过ThreadPoolExecutor创建的线程池;
- 通过Executors创建的线程池。
线程池的创建方式总共包含以下7种(其中6种是通过Executors 创建的,1种是通过ThreadPoolExecutor创建的):
- Executors.newFixedThreadPool :创建一个固定大小的线程池,可以控制并发的线程数,超出的线程会在队列中等待;
- Executors.newCachedThreadPool :创建一个可缓存的线程池,若线程超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
- Executors.newScheduledThreadPool :创建一个可以执行延迟任务的线程池;
- Executors.newSingleThreadExecutor :创建单个线程的线程池,它可以保证先进先出的执行顺序
- Executors.newSingleThreadScheduledExecutor :创建一个可以执行延迟任务的单个线程的线程池;
- Executors.newWorkStealingPool :创建一个抢占式执行的线程池;
- ThreadPoolExecutor :手动创建线程池 的方式,它最多包含了7个参数可供设置,最少可设置5个参数。
Executors
创建固定数量的线程池
java
public class Demo1 {
public static void main(String[] args) {
//创建一个包含5个线程的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5);
//给予10个资源分配给5个线程去分
for(int i = 0 ; i < 10 ; i ++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程名称:" + Thread.currentThread().getName());
}
});
}
//执行完毕后记得关闭。预防占用资源
threadPool.shutdown();
}
}
java
public class Demo2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建5个线程
ExecutorService threadPool = Executors.newFixedThreadPool(5);
//使用线程池执行任务,也给线程池添加任务
for(int i = 0; i < 5 ; i ++) {
//使用Callable据接口来创建线程
Future<Integer> result = threadPool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int num = new Random().nextInt(9);
System.out.println("随机数:" + num);
return num;
}
});
System.out.println("线程池的返回结果:" + result.get());
}
//执行完毕后记得关闭。预防占用资源
threadPool.shutdown();
}
}
通过上述两种方式,我发现向线程池中添加任务的方式有两种:
- execute : 只执行不带返回值的任务
- submit:可以执行又返回值的任务或者没有返回值的任务。
线程池工厂
在上面的执行过程中,可以看到没有设置线程名称时候,他就会是一个默认名,线程工厂可以让我们自定义线程名称或者优先级。
java
public class Demo3 {
public static void main(String[] args) {
//1.创建线程工厂
ThreadFactory threadFactory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
//设置线程命名规则
thread.setName("我的线程-"+r.hashCode());
//设置线程优先级
thread.setPriority(Thread.MAX_PRIORITY);
return thread;
}
};
ExecutorService threadPool = Executors.newFixedThreadPool(5, threadFactory);
for(int i=0;i<5;i++){
threadPool.submit(()->{
//任务
Thread thread = Thread.currentThread();
System.out.println("线程池开始执行了:"+thread.getName()+" "+thread.getPriority());
});
}
//当程序执行完毕后记得关闭线程池
threadPool.shutdown();
}
}
线程池开始执行了:我的线程-1826771953 10
线程池开始执行了:我的线程-455659002 10
线程池开始执行了:我的线程-245257410 10
线程池开始执行了:我的线程-1705736037 10
线程池开始执行了:我的线程-1406718218 10
带缓存的线程池
java
public class Demo4 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i=0; i<1000; i++){
int finalI=i;
threadPool.submit(()->{
System.out.println("i:"+finalI+"|线程名称:"+Thread.currentThread().getName());
});
}
//当程序执行完毕后记得关闭线程池
threadPool.shutdown();
}
}
在此我们发现,我们给线程一千个任务的时候,他并不会创建1000个线程,我们通过执行发现最多会创建100多个线程,然后会进行复用。
故此我们在这里总结一下:
线程池中的线程也不能无限制地被创建。线程池中的线程数量应该根据实际情况进行适当的设置,以便充分利用系统资源,提高系统的性能和稳定性。如果线程池中的线程数量过多,会导致系统资源的耗尽,从而导致系统的性能下降或崩溃。另外,线程的创建和销毁也会带来一定的开销,如果线程池中的线程数量过多,会增加线程的创建和销毁开销,从而降低系统的性能和稳定性。
因此,在实际应用中,需要根据实际情况来设置线程池的大小,以满足应用的需要,并保证系统的性能和稳定性。一般来说,线程池中的线程数量应该根据系统的 CPU 核心数、内存大小、任务类型和系统负载等因素来进行设置,以达到最优的效果。
执行定时任务的线程池
延迟执行一次:
java
public class Demo5 {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加任务的时间:"+ LocalDate.now());
scheduleTest(service);//只执行一次的定时任务
}
/**
* 只执行一次的定时任务
* @param service
*/
private static void scheduleTest(ScheduledExecutorService service) {
//执行定时任务(延迟3秒执行)
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("执行了任务:"+LocalDateTime.now());
}
},3, TimeUnit.SECONDS);
//当循环执行完毕后记得关闭线程池
service.shutdown();
}
}
本延迟执行任务,只能执行一次。
创建这个线程池有3个参数
- 执行任务
- 延迟n秒执行
- 配合执行的时间单位
固定频率执行
我们可以让线程以以固定频率间隔n秒执行,创建这个线程池有四个参数:
- 执行任务
- 延迟n秒执行
- 执行定时任务的频率
- 配合3执行的时间单位
java
public class Demo6 {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加任务的时间:"+ LocalDateTime.now());
//2S之后开始执行定时任务,定时任务每4s执行一次
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:"+LocalDateTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},2,4,TimeUnit.SECONDS);
//由于是定时任务,故而不能够让他关闭,我们需要通过循环来控制关闭,这里我们就不在描述
// service.shutdown();
}
}
java
public class Demo7 {
public static void main(String [] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加任务的时间:"+ LocalDateTime.now());
// //2S之后开始执行定时任务,定时任务每4s执行一次
// service.scheduleAtFixedRate(new Runnable() {
// @Override
// public void run() {
// System.out.println("执行任务:"+LocalDateTime.now());
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// },2,4,TimeUnit.SECONDS);
//2s之后开始执行,每次执行间隔4s
service.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:"+LocalDateTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},2,4,TimeUnit.SECONDS);
}
}
在 Java 中,Timer 类提供了两个方法用于定时执行任务,分别是 scheduleAtFixedRate () 和 scheduleWithFixedDelay()。这两个方法都可以实现定时执行任务的功能,但它们的执行方式略有不同,具体区别如下:
- scheduleAtFixedRate() 方法:该方法是按照固定的时间间隔执行任务,它会按照指定的时间间隔不断地执行任务,无论上一次任务是否执行完成,也不考虑任务的执行时间,即使任务的执行时间超过了时间间隔,也会按照指定的时间间隔再次执行任务。
- scheduleWithFixedDelay() 方法:该方法是在任务执行完成后,等待指定的时间间隔再次执行任务,它会在任务执行完成后,等待指定的时间间隔,然后再次执行任务,考虑了任务的执行时间。也就是说,该方法会在上一次任务执行完成后,等待指定的时间间隔,然后再次执行任务。
因此,如果需要按照固定的时间间隔执行任务,无论任务是否执行完成,都需要按照指定的时间间隔再次执行任务,可以使用 scheduleAtFixedRate() 方法;如果需要在任务执行完成后,等待指定的时间间隔再次执行任务,可以使用 scheduleWithFixedDelay() 方法。
单线程的线程池
java
public class Demo8 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
int finalI=i;
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("任务名:"+finalI+"线程名:"+Thread.currentThread().getName());
}
});
}
//老样子执行完毕后记得关闭
executorService.shutdown();
}
}
单线程的线程池和单线程有什么区别?以及其各自的作用?
单线程的线程池和单线程之间有以下几点区别:
- 线程池是一种可以管理和调度多个线程的机制,可以重复利用线程,从而避免频繁创建和销毁线程的开销,提高程序的性能和稳定性。而单线程则只能执行一个任务,无法进行任务的并行处理。
- 线程池中的线程数量可以根据实际需求进行动态调整,可以增加或减少线程的数量,以适应不同的负载和任务类型。而单线程则只能处理一个任务,无法进行线程数量的动态调整。
- 线程池可以管理和调度多个任务。
定时任务的单线程线程池
java
public class Demo9 {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
System.out.println("执行任务的时间:" + LocalDateTime.now());
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:" + LocalDateTime.now());
}
// 执行任务 2秒后执行 ,单位 TimeUnit.Seconds秒
}, 2, TimeUnit.SECONDS);
//当定时任务执行完毕后记得关闭
service.shutdown();
}
}
根据当前CPU生成线程池
java
public class Demo10 {
public static void main(String[] args) {
//根据当前电脑的CPU来创建线程池
ExecutorService service = Executors.newWorkStealingPool();
for(int i = 0 ; i < 100; i ++ ) {
int finaIi = i ;
service.submit(()->{
System.out.println("任务:" + finaIi + "线程名:" + Thread.currentThread());
});
}
//判断线程是否
while(!service.isTerminated()) {
//执行完毕后记得关闭线程池
service.shutdown();
};
}
}
ThreadPoolExecutor
ThreadPoolExecutor和Executors比较
都是Java中用于生成线程池的类,它们有以下几个区别:
- ThreadPoolExecutor是一个更底层的类 ,它提供了更多的配置选项,如线程池的核心线程数、最大线程数、任务队列等,可以更加灵活地控制线程池的行为。而Executors则是在ThreadPoolExecutor基础上进行了封装,提供了一些预定义的线程池配置选项,如newFixedThreadPool、newCachedThreadPool等,方便用户快速创建线程池。
- ThreadPoolExecutor可以通过自定义RejectedExecutionHandler来处理任务被拒绝执行的情况,而Executors只提供了一些简单的拒绝策略,如抛出异常、丢弃任务等。
- Executors生成的线程池通常是无界队列 的,如果任务数量过多,可能会导致内存溢出 等问题。而ThreadPoolExecutor则可以通过配置有界队列或者拒绝策略来避免这种情况。
- 在Java 8之前,Executors生成的线程池中的工作线程都是非守护线程 ,即使主线程结束,工作线程也会继续执行。而ThreadPoolExecutor则可以通过设置工作线程为守护线程来避免这种情况。
ThreadPoolExecutor和Executors各自可以解决的问题也有所差异。ThreadPoolExecutor适合于需要更精细的线程池配置、需要自定义拒绝策略、需要有界队列等情况。而Executors则适合于快速创建线程池,且任务数量不会过多的情况。需要根据具体场景选择合适的类来生成线程池。
Executors存在的问题
虽然Executors可以快速创建线程池,但在一些情况下可能会存在问题,如下:
- FixedThreadPool和SingleThreadExecutor的线程数是固定的,如果任务数量过多,会导致队列中的任务堆积,从而占用大量内存,甚至导致OOM异常。
- CachedThreadPool的最大线程数是Integer.MAX_VALUE,如果任务数量过多,会创建大量线程,占用大量系统资源,导致系统崩溃。
- Executors生成的线程池中的工作线程都是非守护线程,即使主线程结束,工作线程也会继续执行。如果应用程序没有显式地终止线程池,会导致应用程序无法正常退出,从而导致内存泄漏等问题。
- Executors提供的拒绝策略有一些局限性,例如在任务被拒绝后,无法重新提交任务等。
因此,对于不同的应用场景,应该选择合适的线程池类型或者直接使用ThreadPoolExecutor来手动创建线程池,以避免出现上述问题。
ThreadPoolExecutor的介绍
ThreadPoolExecutor是Java中用于创建线程池的类,它有以下几个参数:
- corePoolSize :线程池的核心线程数,即线程池中始终存在的线程数。如果线程池中的线程数小于corePoolSize,则会创建新的线程来执行任务,直到线程数等于corePoolSize。
- maximumPoolSize :线程池的最大线程数,即线程池中最多能存在的线程数。如果任务数量超过了线程池的容量,则会根据拒绝策略处理这些任务。
- keepAliveTime :线程池中非核心线程的空闲存活时间。如果线程池中的线程数大于corePoolSize,并且某个线程空闲的时间超过了keepAliveTime,则该线程会被销毁直到线程池中的线程数等于corePoolSize。
- TimeUnit :keepAliveTime的时间单位,例如TimeUnit.SECONDS。
- workQueue :线程池中的任务队列,用于存储等待执行的任务。可以选择不同类型的队列,如有界队列和无界队列。
- threadFactory :线程工厂,用于创建新的线程。可以自定义线程的名称、优先级等属性。
- handler :拒绝策略,用于处理无法执行的任务。可以选择不同的策略,如抛出异常、丢弃任务、等待一段时间再重试等。
这些参数的具体作用如下:
- 通过corePoolSize和maximumPoolSize来控制线程池中的工作线程数量,以达到最优的性能和资源利用效率。
- 通过keepAliveTime和workQueue来控制线程池中线程的存活时间和任务的排队策略,以避免任务堆积和资源浪费。
- 通过threadFactory可以自定义线程的属性,如线程名称等。
当我们使用ThreaPoolExecutor创建线程池的时候,默认最少使用5个参数
java
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue)
java
public class Demo1 {
public static void main(String[] args) {
ThreadFactory factory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
return thread;
}
};
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
5,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(2),
factory,
new ThreadPoolExecutor.DiscardPolicy());
for(int i = 1 ; i < 6 ; i ++) {
int finaIi = i ;
executor.submit(()->{
try {
Thread.sleep(10 * finaIi);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务:" + finaIi);
});
}
//执行循环完毕后关闭线程池
executor.shutdown();
}
}
线程池的执行流程
线程池的执行流程如下:
- 首先,线程池会创建一些初始的核心线程,并将它们放入工作队列中。
- 当有任务进来时,线程池会从工作队列中选取一个线程来执行任务。
- 如果此时线程池中的线程数小于核心线程数(corePoolSize)且有空闲线程,则会立即创建新的线程来执行任务。
- 如果此时线程池中的线程数
已经达到了核心线程数,且工作队列已满
,则会将任务提交到线程池的任务队列中。 - 如果此时
任务队列已满,且线程池中的线程数小于最大线程数(maximumPoolSize)
,则会创建新的线程来执行任务。 - 如果此时线程池中的线程数
已经达到了最大线程数,且任务队列已满
,则会按照拒绝策略(RejectedExecutionHandler)来处理任务。常见的拒绝策略有:抛出异常、丢弃任务、等待一段时间再重试等。 - 当某个线程执行完任务后,会从任务队列中取出下一个任务继续执行,直到线程池关闭或者出现异常。
需要注意的是,线程池的执行流程是异步的,任务的执行顺序是不确定的。线程池在执行任务时,会根据任务的优先级、执行时间等因素来选择执行顺序,具体的执行流程会根据不同的线程池实现而有所差异。
拒绝策略
目前线程池的拒绝策略共计有5种,其中有四种JDK提供的和一种自定义拒绝策略。
线程池的拒绝策略指的是当线程池中的线程已经全部被占用,队列也已满,无法继续接受新的任务时,应该如何处理这些被拒绝的任务
。常见的线程池拒绝策略包括:
- AbortPolicy(默认策略):直接抛出RejectedExecutionException异常,表示拒绝执行该任务。
- CallerRunsPolicy:由调用线程处理该任务,即在提交任务的线程中直接执行该任务。这种策略可能会降低整个系统的吞吐量,因为提交任务的线程可能不是专门用来处理任务的线程,而是业务线程。
- DiscardPolicy:直接丢弃该任务,不做任何处理。
- DiscardOldestPolicy:抛弃最早加入队列的任务,并尝试再次提交当前任务。
- 自定义拒绝策略:用户可以根据业务场景,自定义拒绝策略,例如将任务记录到日志中、将任务放到消息队列中等等。
选择合适的线程池拒绝策略,可以更好地保障系统的稳定性和可靠性。
第一种AbortPolicy
java
public class Demo2 {
public static void main(String[] args) {
ThreadFactory factory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
return thread;
}
};
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
2,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(2),
factory,
new ThreadPoolExecutor.AbortPolicy() //拒绝策略
);
for(int i = 0 ; i < 5; i ++) {
int finaIi = i;
executor.submit(()->{
try {
Thread.sleep(10*finaIi);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务:" + finaIi);
});
}
//执行循环完毕后关闭线程池
executor.shutdown();
}
}
第二种CallerRunsPolicy()
java
import java.util.concurrent.*;
/**
* 手动方式创建线程池
*/
public class Demo3 {
public static void main(String[] args) {
ThreadFactory factory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
return thread;
}
};
ThreadPoolExecutor executor=new ThreadPoolExecutor(
2,
2,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(2),
factory,
new ThreadPoolExecutor.CallerRunsPolicy()); //拒绝策略
for (int i = 0; i < 5; i++) {
int finalI=i;
executor.submit(()->{
try {
Thread.sleep(10*finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务:"+finalI);
});
}
//执行循环完毕后关闭线程池
executor.shutdown();
}
}
第三种:DiscardPolicy ()
java
public class Demo4 {
public static void main(String[] args) {
ThreadFactory factory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
return thread;
}
};
ThreadPoolExecutor executor=new ThreadPoolExecutor(
2,
2,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(2),
factory,
new ThreadPoolExecutor.DiscardPolicy()
);
for (int i = 0; i < 5; i++) {
int finalI=i;
executor.submit(()->{
try {
Thread.sleep(10*finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务:"+finalI);
});
}
//执行循环完毕后关闭线程池
executor.shutdown();
}
}
第四种:DiscardOldestPolicy()
java
public class Demo5 {
public static void main(String[] args) {
ThreadFactory factory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
return thread;
}
};
ThreadPoolExecutor executor=new ThreadPoolExecutor(
2,
2,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(2),
factory,
new ThreadPoolExecutor.DiscardOldestPolicy() //拒绝策略
);
for (int i = 0; i < 5; i++) {
int finalI=i;
executor.submit(()->{
try {
Thread.sleep(10*finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务:"+finalI);
});
}
//执行循环完毕后关闭线程池
executor.shutdown();
}
}
第五种:自定义拒绝策略
java
public class Demo6 {
public static void main(String[] args) {
ThreadFactory factory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
return thread;
}
};
ThreadPoolExecutor executor=new ThreadPoolExecutor(
2,
2,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(2),
factory,
new RejectedExecutionHandler() { //自定义拒绝策略
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("我执行了自定义拒绝策略");
}
});
for (int i = 0; i < 5; i++) {
int finalI=i;
executor.submit(()->{
try {
Thread.sleep(10*finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务:"+finalI);
});
}
//执行循环完毕后关闭线程池
executor.shutdown();
}
}
线程池的状态
线程池的状态通常包括以下几种:
- RUNNING :线程池处于运行状态,可以接受新任务并处理已有的任务。
- SHUTDOWN :线程池处于关闭状态,不再接受新任务,但会处理已有的任务。
- STOP :线程池处于停止状态,不再接受新任务,也不会处理已有的任务,会中断正在执行的任务。
- TIDYING :线程池正在整理线程池中的线程,例如删除已经停止的线程。
- TERMINATED :线程池已经终止,不再接受任何任务。
线程池的状态可以通过ThreadPoolExecutor类的getState()方法获取,返回一个枚举类型的值,表示当前线程池的状态。在使用线程池的过程中,需要根据当前线程池的状态,避免不必要的操作,保证线程池的稳定性和可靠性
- RUNNING状态的例子:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println("Task executed.");
}
});
System.out.println("Current thread pool state: " + executor.getState());
// 输出:Current thread pool state: RUNNING
这个例子中,线程池处于RUNNING状态,因为线程池已经创建成功,可以接受新任务并处理已有的任务。
- SHUTDOWN状态的例子:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.shutdown();
System.out.println("Current thread pool state: " + executor.getState());
// 输出:Current thread pool state: SHUTDOWN
这个例子中,线程池处于SHUTDOWN状态,因为调用了executor.shutdown()方法,线程池不再接受新任务,但会处理已有的任务。
- STOP状态的例子:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.execute(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("Task executed.");
}
}
});
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Current thread pool state: " + executor.getState());
// 输出:Current thread pool state: STOP
这个例子中,线程池处于STOP状态,因为调用了executor.shutdown()方法后,线程池不再接受新任务,并且中断正在执行的任务。
- TIDYING状态的例子:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.shutdown();
while (!executor.isTerminated()) {
System.out.println("Current thread pool state: " + executor.getState());
}
// 输出:
// Current thread pool state: TIDYING
// Current thread pool state: TERMINATED
这个例子中,线程池处于TIDYING状态,因为调用了executor.shutdown()方法后,线程池正在整理线程池中的线程,例如删除已经停止的线程。
- TERMINATED状态的例子:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Current thread pool state: " + executor.getState());
// 输出:Current thread pool state: TERMINATED
这个例子中,线程池处于TERMINATED状态,因为线程池已经终止,不再接受任何任务。在调用executor.shutdown()方法后,等待所有任务执行完毕并且所有线程都被回收后,线程池进入TERMINATED状态。
关闭线程池的方法
ThreadPoolExecutor类提供了两种关闭线程池的方法,分别是shutdown()和shutdownNow()。这两种方法的区别如下:
- shutdown()方法:调用该方法后,线程池会拒绝新的任务提交,但会继续处理已经提交的任务,直到所有任务都被完成 。在所有任务完成后,线程池才会真正关闭,并释放所有资源。如果在调用shutdown()方法之后,继续提交新的任务,这些任务会被拒绝并抛出RejectedExecutionException异常。
- shutdownNow()方法:调用该方法后,线程池会拒绝新的任务提交,并尝试中断正在执行的任务。在中断任务的过程中,如果任务响应中断,则任务会被成功中断并从任务队列中移除。如果任务无法响应中断,则任务会继续执行。在所有任务都被中断或已经完成后,线程池会真正关闭,并释放所有资源。如果在调用shutdownNow()方法之后,继续提交新的任务,这些任务会被拒绝并抛出RejectedExecutionException异常。
因此,shutdown()方法是优雅地关闭线程池,等待所有任务都被完成后再关闭线程池,而shutdownNow()方法是强制关闭线程池,立即停止所有任务的执行,并尝试中断正在执行的任务。选择哪个方法取决于业务需求,如果需要优雅地关闭线程池,可以使用shutdown()方法,如果需要立即停止所有任务的执行,可以使用shutdownNow()方法。