Java JUC并发之线程池详解(重点)
十一、线程池(重点)
线程池 : 三大方法、7大参数、4种拒绝策略
池化技术
程序运行 => 本质:占用系统的资源!
如何优化资源的使用? => 池化技术
常用的池:
- 线程池
- 连接池
- 内存池
- 对象池
- ...
注意: 经常创建、销毁 会造成资源浪费
池化技术: 事先准备好一些资源,如果有人要用,就从这里拿,用完之后还回来
线程池的好处:
即 : 线程复用、可以控制最大并发数、管理线程
线程池不允许使用executors去创建,而是通过ThreadPoolExecutor的方式
(让写的同学更加明确线程池的运行规则,规避资源耗尽的风险)详细说明见阿里巴巴Java开发手册。
Executors 返回的线程池对象的弊端:
-
FixedThreadPool和SingleThreadPool:
允许的请求队列长度为Integer.MAX_VALUE => 21亿左右,会堆积大量的请求,导致OOM 【OOM : OutOfMemory,内存溢出】
-
CachedThreadPool 和 ScheduledThreadPool :
允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM.
三大方法:
- Executors.newSingleThreadExecutor() => 单一线程
- Executors.newFixedThreadPool(5) => 固定大小的线程池
- Executors.newCachedThreadPool() => 线程池大小由CPU处理能力决定 (弹性变化)
- Executors.newScheduledThreadPool(10) => 创建定长的线程池,而且支持定时以及周期性的任务执行
package com.liu.threadpool; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; // Executors 工具类 四大方法 public class TestThreadPool { public static void main(String[] args) { //ExecutorService threadPool = Executors.newSingleThreadExecutor(); // 单个线程 //ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建固定数目的线程 //ExecutorService threadPool = Executors.newCachedThreadPool(); // 根据CPU的能力来创建线程 遇强则强 ExecutorService threadPool = Executors.newScheduledThreadPool(10);// 创建定长的线程池,而且支持定时以及周期性的任务执行 try { for (int i = 1; i <= 10000; i++) { threadPool.execute(()->{ System.out.println(Thread.currentThread().getName() + " ok"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } } }
线程池面试题!
四种实现方式 七大参数 四大拒绝策略
源码分析:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, // 21亿
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
本质 : ThreadPoolExecutor() !!!
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue ) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(),
defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
int maximumPoolSize, // 最大核心线程池大小
long keepAliveTime, // 空闲线程的存活时间
TimeUnit unit, // 超时等待的时间单位,时间到了会将线程释放,只留下核心线程
BlockingQueue<Runnable> workQueue, // 阻塞队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
理解线程池的原理: 以银行办理业务为例:
手动创建线程池
package com.liu.threadpool;
import java.util.concurrent.*;
/**
* 四种拒绝策略
* 1. new ThreadPoolExecutor.AbortPolicy() 拒绝策略 银行满了,还有客人进来,不处理该业务并抛出异常
* 2. new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 哪来的去哪里! 由main线程处理
* 3. new ThreadPoolExecutor.DiscardPolicy() // 拒绝策略 队列满了 不会抛出异常! 直接丢掉任务
* 4. new ThreadPoolExecutor.DiscardOldestPolicy() // 拒绝策略 队列满了 尝试和最早的(执行了最长时间的线程)竞争,最后失败了也不会抛出异常
*/
public class TestThreadPool02 {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3), // 阻塞队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.DiscardOldestPolicy() // 拒绝策略 队列满了 尝试和最早的(执行了最长时间的线程)竞争,最后失败了也不会抛出异常
);
try {
// 最大承载 :Queue + Max
for (int i = 1; i <= 9; i++) {
// 使用了线程池之后,使用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName() + " ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}
四种拒绝策略
- AbortPolicy() 银行满了,还有客人进来,不处理该业务并抛出异常
- CallerRunsPolicy() 哪来的去哪里! 由main线程 或其他线程 处理
- DiscardPolicy() 队列满了 不会抛出异常! 直接丢掉任务
- DiscardOldestPolicy() 队列满了 尝试和当前最老的线程执行的任务竞争,最后失败了也不会抛出异常
小结和拓展
-
最大线程到底该如何设置? 依据什么来定义?(调优!)
-
CPI 密集型 CPU为多少核,就将最大线程池大小定义为多少
可以保持CPU的效率最高!
// 获取CPU的核数 // CPU密集型、IO密集型 System.out.println(Runtime.getRuntime().availableProcessors());
-
IO 密集型 **判断程序中十分占用IO资源的的线程数 **一般设置为 2 倍
程序=> 15个大型任务 IO 十分占用资源 => 设置为 30
-
package com.liu.threadpool;
import java.util.concurrent.*;
public class TestThreadPool02 {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
Runtime.getRuntime().availableProcessors(), // cpu核数 即最大线程池大小
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3), // 阻塞队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.DiscardOldestPolicy() // 拒绝策略 队列满了 尝试和最早的(执行了最长时间的线程)竞争,最后失败了也不会抛出异常
);
try {
// 最大承载 :Queue + Max
for (int i = 1; i <= 9; i++) {
// 使用了线程池之后,使用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName() + " ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}
本文来自博客园,作者:{夕立君},转载请注明原文链接:https://www.cnblogs.com/xili-sanriyue/p/15016591.html