线程池主要的知识点:
- 三大方法:
- 七大参数
- 四打拒绝策略
线程池中的三大方法在Executors的工具类中
- Executors.newSingleThreadExecutor();//单一的线程池,单个线程
- Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
- Executors.newCachedThreadPool();//创建一个可扩缩的线程池
public class ThreadPoolDemo01 {
public static void main(String[] args) {
//1.线程池三大方法
ExecutorService threadPool1 = Executors.newSingleThreadExecutor();//单一的线程池,单个线程
ExecutorService threadPool2 = Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
ExecutorService threadPool3 = Executors.newCachedThreadPool();//创建一个可扩缩的线程池
try {
for (int i = 0; i < 5; i++) {
threadPool1.execute(() -> System.out.println(Thread.currentThread().getName() + "-> OK"));
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPool1.shutdown();
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HUpjeaNJ-1663857307379)(img_1.png)]
单一线程池只有一个线程工作
public class ThreadPoolDemo01 {
public static void main(String[] args) {
//1.线程池三大方法
ExecutorService threadPool1 = Executors.newSingleThreadExecutor();//单一的线程池,单个线程
ExecutorService threadPool2 = Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
ExecutorService threadPool3 = Executors.newCachedThreadPool();//创建一个可扩缩的线程池
try {
for (int i = 0; i < 10; i++) {
threadPool2.execute(() -> System.out.println(Thread.currentThread().getName() + "-> OK"));
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPool2.shutdown();
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BG5Qb31r-1663857307380)(img_2.png)]
固定大小线程池时最多有5个线程执行,即最高同时可以有5个并发
public class ThreadPoolDemo01 {
public static void main(String[] args) {
//1.线程池三大方法
ExecutorService threadPool1 = Executors.newSingleThreadExecutor();//单一的线程池,单个线程
ExecutorService threadPool2 = Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
ExecutorService threadPool3 = Executors.newCachedThreadPool();//创建一个可扩缩的线程池
try {
for (int i = 0; i < 1000; i++) {
threadPool3.execute(() -> System.out.println(Thread.currentThread().getName() + "-> OK"));
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPool3.shutdown();
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TXBIvMfV-1663857307380)(img_3.png)]
可扩缩的线程池,可以根据线程的多少自动调整线程池大小
Exxcutors工具类中创建线程池的方法实际是重载的ThreadPoolExecutor构造方法,所谓的七大参数就是该构造方法的参数而已。
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
直接来看测试类
public class ThreadPoolDemo02 {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(2,//核心线程池大小
5,//最大核心线程池大小
2,//超时时间,超时后,若没有被使用就会释放线程池中除了核心的其他
TimeUnit.SECONDS,//超时单位
new LinkedBlockingDeque<>(3),//阻塞队列
Executors.defaultThreadFactory(),//线程工厂,创建线程的
new ThreadPoolExecutor.AbortPolicy()//拒绝策略
);
}
}
这几个参数的理解,如上面测试方法所示
- 当线程数>=5时,只有核心线程池会工作,即第一个参数
- 当线程数>5时,第五个参数是一个规定大小的阻塞队列,此时阻塞队列中会有等待的线程,此时才会额外的开放线程池,但不会超过最大核心线程池即第二个参数
- 当线程数>8时,阻塞队列中线程数满了,会触发拒绝策略。即第七个参数
- 另外,第三个参数时超时时间,第四个参数超时时间单位,超过对应时间,若没有线程使用线程池,则会释放线程池。第六个参数是创建线程的线程工厂,一般使用默认的。
所谓拒绝策略就是在阻塞队列满了之后,线程池对没有进入线程池的线程的四种处理。
- AbortPolicy:第一种拒绝策略:超过最大可处理线程数后抛异常;
- DiscardPolicy:第二种拒绝策略:超过最大可处理线程数后报错不抛异常
- CallerRunsPolicy:第三种拒绝策略:超过最大可处理线程数后,将超出线程退回并交给上层线程处理(即调用者线程去处理)
- DiscardOldestPolicy()//第四种拒绝策略:超过最大可处理线程数后,不抛异常,但是后进入的线程会尝试调用最先进入的线程,如果最先进入的线程已经执行完毕,则调用此线程.
public class ThreadPoolDemo03 {
public static void main(String[] args) {
ExecutorService threadPool1 = new ThreadPoolExecutor(2,
5,
2,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()//第一种拒绝策略:超过最大可处理线程数后抛异常
);
ExecutorService threadPool2 = new ThreadPoolExecutor(2,
5,
2,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy()//第二种拒绝策略:超过最大可处理线程数后报错不抛异常
);
ExecutorService threadPool3 = new ThreadPoolExecutor(2,
5,
2,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()//第三种拒绝策略:超过最大可处理线程数后,将超出线程退回并交给上层线程处理(即调用者线程去处理)
);
ExecutorService threadPool4 = new ThreadPoolExecutor(2,
5,
2,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()//第四种拒绝策略:超过最大可处理线程数后,不抛异常,但是后进入的线程会尝试调用最先进入的线程,如果最先进入的线程已经执行完毕,则调用此线程
);
for (int i = 0; i < 10000; i++) {
threadPool4.execute(() -> System.out.println(Thread.currentThread().getName() + "-> OK"));
}
}
}
- CPU密集型:cpu内核数量是最大线程池大小。
- IO密集型:判断程序中十分耗时的IO线程数,最大线程池大小 > 此线程数
public class ThreadPoolDemo04 {
public static void main(String[] args) {
//获取cpu内核数量
System.out.println(Runtime.getRuntime().availableProcessors());
ExecutorService threadPool = new ThreadPoolExecutor(2,
Runtime.getRuntime().availableProcessors(),
2,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
}
}
线程池的本质是池化技术,所谓池化技术,是程序在运行时会占用资源,为了优化资源的使用使用池化技术。比如JDBC连接池,
由于和数据库建立连接和关闭时比较消耗资源,所以事先准备好资源。
线程池的优点:降低资源消耗,提高响应速度,而且方便管理,最重要的是线程服用,可以控制并发数量。