涉及到线程池之前,我们需要知道创建线程的几种方式,在线程的基础之上,我们进一步学习线程池。

线程池

线程池是什么?

线程池是一种池化技术,使用池化技术管理和使用线程的一种机制。

池化技术:在利用资源之前,我们需要提前准备一些资源,在需要时候重复使用提前准备的资源。

池化技术(Pooling)是一种常用的优化技术,它的目的是通过重复使用已经创建的对象或者资源,来避免频繁的创建和销毁对象或者资源所带来的开销。通过池化技术,可以减少创建和销毁对象的开销,从而提高程序的性能和稳定性。在计算机领域中,池化技术通常用于以下几个方面:

  1. 数据库连接池:通过重复使用已经创建的数据库连接,避免频繁地创建和销毁数据库连接所带来的开销。

  2. 线程池:通过重复使用已经创建的线程,避免频繁地创建和销毁线程所带来的开销。

  3. 对象池:通过重复使用已经创建的对象,避免频繁地创建和销毁对象所带来的开销。

  4. 内存池:通过重复使用已经分配的内存,避免频繁地分配和释放内存所带来的开销。

在实际应用中,池化技术可以帮助我们提高程序的性能和稳定性,因此在设计和开发程序时,可以考虑采用池化技术来优化程序的性能。

线程池的使用方式

  • 通过ThreadPoolExecutor创建的线程池;
  • 通过Executors创建的线程池。

线程池的创建方式总共包含以下7种(其中6种是通过Executors 创建的,1种是通过ThreadPoolExecutor创建的):

  • Executors.newFixedThreadPool:创建一个固定大小的线程池,可以控制并发的线程数,超出的线程会在队列中等待;
  • Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
  • Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;
  • Executors.newSingleThreadExecutor:创建单个线程的线程池,它可以保证先进先出的执行顺序
  • Executors.newSingleThreadScheduledExecutor:创建一个可以执行延迟任务的单个线程的线程池;
  • Executors.newWorkStealingPool:创建一个抢占式执行的线程池;
  • ThreadPoolExecutor:手动创建线程池 的方式,它最多包含了7个参数可供设置,最少可设置5个参数。

Executors

创建固定数量的线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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:可以执行又返回值的任务或者没有返回值的任务。

线程池工厂

在上面的执行过程中,可以看到没有设置线程名称时候,他就会是一个默认名,线程工厂可以让我们自定义线程名称或者优先级。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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

带缓存的线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
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 核心数、内存大小、任务类型和系统负载等因素来进行设置,以达到最优的效果。

执行定时任务的线程池

延迟执行一次:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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秒执行,创建这个线程池有四个参数:

  1. 执行任务
  2. 延迟n秒执行
  3. 执行定时任务的频率
  4. 配合3执行的时间单位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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()。这两个方法都可以实现定时执行任务的功能,但它们的执行方式略有不同,具体区别如下:

  1. scheduleAtFixedRate() 方法:该方法是按照固定的时间间隔执行任务,它会按照指定的时间间隔不断地执行任务,无论上一次任务是否执行完成,也不考虑任务的执行时间,即使任务的执行时间超过了时间间隔,也会按照指定的时间间隔再次执行任务。

  2. scheduleWithFixedDelay() 方法:该方法是在任务执行完成后,等待指定的时间间隔再次执行任务,它会在任务执行完成后,等待指定的时间间隔,然后再次执行任务,考虑了任务的执行时间。也就是说,该方法会在上一次任务执行完成后,等待指定的时间间隔,然后再次执行任务。

因此,如果需要按照固定的时间间隔执行任务,无论任务是否执行完成,都需要按照指定的时间间隔再次执行任务,可以使用 scheduleAtFixedRate() 方法;如果需要在任务执行完成后,等待指定的时间间隔再次执行任务,可以使用 scheduleWithFixedDelay() 方法。

单线程的线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
}
}

单线程的线程池和单线程有什么区别?以及其各自的作用?

单线程的线程池和单线程之间有以下几点区别:

  1. 线程池是一种可以管理和调度多个线程的机制,可以重复利用线程,从而避免频繁创建和销毁线程的开销,提高程序的性能和稳定性。而单线程则只能执行一个任务,无法进行任务的并行处理。

  2. 线程池中的线程数量可以根据实际需求进行动态调整,可以增加或减少线程的数量,以适应不同的负载和任务类型。而单线程则只能处理一个任务,无法进行线程数量的动态调整。

  3. 线程池可以管理和调度多个任务。

定时任务的单线程线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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生成线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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中用于生成线程池的类,它们有以下几个区别:

  1. ThreadPoolExecutor是一个更底层的类,它提供了更多的配置选项,如线程池的核心线程数、最大线程数、任务队列等,可以更加灵活地控制线程池的行为。而Executors则是在ThreadPoolExecutor基础上进行了封装提供了一些预定义的线程池配置选项,如newFixedThreadPool、newCachedThreadPool等,方便用户快速创建线程池。

  2. ThreadPoolExecutor可以通过自定义RejectedExecutionHandler来处理任务被拒绝执行的情况,而Executors只提供了一些简单的拒绝策略,如抛出异常、丢弃任务等。

  3. Executors生成的线程池通常是无界队列的,如果任务数量过多,可能会导致内存溢出等问题。而ThreadPoolExecutor则可以通过配置有界队列或者拒绝策略来避免这种情况。

  4. 在Java 8之前,Executors生成的线程池中的工作线程都是非守护线程,即使主线程结束,工作线程也会继续执行。而ThreadPoolExecutor则可以通过设置工作线程为守护线程来避免这种情况。

ThreadPoolExecutor和Executors各自可以解决的问题也有所差异。ThreadPoolExecutor适合于需要更精细的线程池配置、需要自定义拒绝策略、需要有界队列等情况。而Executors则适合于快速创建线程池,且任务数量不会过多的情况。需要根据具体场景选择合适的类来生成线程池。

Executors存在的问题

虽然Executors可以快速创建线程池,但在一些情况下可能会存在问题,如下:

  1. FixedThreadPoolSingleThreadExecutor的线程数是固定的,如果任务数量过多,会导致队列中的任务堆积,从而占用大量内存,甚至导致OOM异常。

  2. CachedThreadPool的最大线程数是Integer.MAX_VALUE,如果任务数量过多,会创建大量线程,占用大量系统资源,导致系统崩溃

  3. Executors生成的线程池中的工作线程都是非守护线程,即使主线程结束,工作线程也会继续执行。如果应用程序没有显式地终止线程池,会导致应用程序无法正常退出,从而导致内存泄漏等问题。

  4. Executors提供的拒绝策略有一些局限性,例如在任务被拒绝后,无法重新提交任务等

因此,对于不同的应用场景,应该选择合适的线程池类型或者直接使用ThreadPoolExecutor来手动创建线程池,以避免出现上述问题。

ThreadPoolExecutor的介绍

ThreadPoolExecutor是Java中用于创建线程池的类,它有以下几个参数:

  1. corePoolSize:线程池的核心线程数,即线程池中始终存在的线程数。如果线程池中的线程数小于corePoolSize,则会创建新的线程来执行任务,直到线程数等于corePoolSize。

  2. maximumPoolSize:线程池的最大线程数,即线程池中最多能存在的线程数。如果任务数量超过了线程池的容量,则会根据拒绝策略处理这些任务。

  3. keepAliveTime:线程池中非核心线程的空闲存活时间。如果线程池中的线程数大于corePoolSize,并且某个线程空闲的时间超过了keepAliveTime,则该线程会被销毁直到线程池中的线程数等于corePoolSize。

  4. TimeUnit:keepAliveTime的时间单位,例如TimeUnit.SECONDS。

  5. workQueue:线程池中的任务队列,用于存储等待执行的任务。可以选择不同类型的队列,如有界队列和无界队列。

  6. threadFactory:线程工厂,用于创建新的线程。可以自定义线程的名称、优先级等属性。

  7. handler:拒绝策略,用于处理无法执行的任务。可以选择不同的策略,如抛出异常、丢弃任务、等待一段时间再重试等。

这些参数的具体作用如下:

  1. 通过corePoolSize和maximumPoolSize来控制线程池中的工作线程数量,以达到最优的性能和资源利用效率。
  2. 通过keepAliveTime和workQueue来控制线程池中线程的存活时间和任务的排队策略,以避免任务堆积和资源浪费。
  3. 通过threadFactory可以自定义线程的属性,如线程名称等。

当我们使用ThreaPoolExecutor创建线程池的时候,默认最少使用5个参数

1
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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();
}
}

线程池的执行流程

线程池的执行流程如下:

  1. 首先,线程池会创建一些初始的核心线程,并将它们放入工作队列中。

  2. 当有任务进来时,线程池会从工作队列中选取一个线程来执行任务。

  3. 如果此时线程池中的线程数小于核心线程数(corePoolSize)且有空闲线程,则会立即创建新的线程来执行任务。

  4. 如果此时线程池中的线程数已经达到了核心线程数,且工作队列已满,则会将任务提交到线程池的任务队列中。

  5. 如果此时任务队列已满,且线程池中的线程数小于最大线程数(maximumPoolSize),则会创建新的线程来执行任务。

  6. 如果此时线程池中的线程数已经达到了最大线程数,且任务队列已满,则会按照拒绝策略(RejectedExecutionHandler)来处理任务。常见的拒绝策略有:抛出异常、丢弃任务、等待一段时间再重试等。

  7. 当某个线程执行完任务后,会从任务队列中取出下一个任务继续执行,直到线程池关闭或者出现异常。

需要注意的是,线程池的执行流程是异步的,任务的执行顺序是不确定的。线程池在执行任务时,会根据任务的优先级、执行时间等因素来选择执行顺序,具体的执行流程会根据不同的线程池实现而有所差异。

拒绝策略

目前线程池的拒绝策略共计有5种,其中有四种JDK提供的和一种自定义拒绝策略。

线程池的拒绝策略指的是当线程池中的线程已经全部被占用,队列也已满,无法继续接受新的任务时,应该如何处理这些被拒绝的任务。常见的线程池拒绝策略包括:

  1. AbortPolicy(默认策略):直接抛出RejectedExecutionException异常,表示拒绝执行该任务。
  2. CallerRunsPolicy:由调用线程处理该任务,即在提交任务的线程中直接执行该任务。这种策略可能会降低整个系统的吞吐量,因为提交任务的线程可能不是专门用来处理任务的线程,而是业务线程。
  3. DiscardPolicy:直接丢弃该任务,不做任何处理。
  4. DiscardOldestPolicy:抛弃最早加入队列的任务,并尝试再次提交当前任务。
  5. 自定义拒绝策略:用户可以根据业务场景,自定义拒绝策略,例如将任务记录到日志中、将任务放到消息队列中等等。

选择合适的线程池拒绝策略,可以更好地保障系统的稳定性和可靠性。

第一种AbortPolicy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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 ()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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();
}
}

第五种:自定义拒绝策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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();
}
}

线程池的状态

线程池的状态通常包括以下几种:

  1. RUNNING:线程池处于运行状态,可以接受新任务并处理已有的任务。

  2. SHUTDOWN:线程池处于关闭状态,不再接受新任务,但会处理已有的任务。

  3. STOP:线程池处于停止状态,不再接受新任务,也不会处理已有的任务,会中断正在执行的任务。

  4. TIDYING:线程池正在整理线程池中的线程,例如删除已经停止的线程。

  5. TERMINATED:线程池已经终止,不再接受任何任务。

线程池的状态可以通过ThreadPoolExecutor类的getState()方法获取,返回一个枚举类型的值,表示当前线程池的状态。在使用线程池的过程中,需要根据当前线程池的状态,避免不必要的操作,保证线程池的稳定性和可靠性

  1. RUNNING状态的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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状态,因为线程池已经创建成功,可以接受新任务并处理已有的任务。

  2. SHUTDOWN状态的例子:

1
2
3
4
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()方法,线程池不再接受新任务,但会处理已有的任务。

  1. STOP状态的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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()方法后,线程池不再接受新任务,并且中断正在执行的任务。

  2. TIDYING状态的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    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()方法后,线程池正在整理线程池中的线程,例如删除已经停止的线程。

  3. TERMINATED状态的例子:

1
2
3
4
5
6
7
8
9
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()。这两种方法的区别如下:

  1. shutdown()方法:调用该方法后,线程池会拒绝新的任务提交,但会继续处理已经提交的任务,直到所有任务都被完成。在所有任务完成后,线程池才会真正关闭,并释放所有资源。如果在调用shutdown()方法之后,继续提交新的任务,这些任务会被拒绝并抛出RejectedExecutionException异常。

  2. shutdownNow()方法:调用该方法后,线程池会拒绝新的任务提交,并尝试中断正在执行的任务。在中断任务的过程中,如果任务响应中断,则任务会被成功中断并从任务队列中移除。如果任务无法响应中断,则任务会继续执行。在所有任务都被中断或已经完成后,线程池会真正关闭,并释放所有资源。如果在调用shutdownNow()方法之后,继续提交新的任务,这些任务会被拒绝并抛出RejectedExecutionException异常。

因此,shutdown()方法是优雅地关闭线程池,等待所有任务都被完成后再关闭线程池,而shutdownNow()方法是强制关闭线程池,立即停止所有任务的执行,并尝试中断正在执行的任务。选择哪个方法取决于业务需求,如果需要优雅地关闭线程池,可以使用shutdown()方法,如果需要立即停止所有任务的执行,可以使用shutdownNow()方法。