文章目录
6.1为什么用线程池?
6.2线程池如何使用
线程池的工具类:
- Arrays:数组的工具类
- Collections:集合的工具类
- Executors:线程池的工具类
6.2.1线程池的架构说明
Java中的线程池是通过Executor框架实现的,该框架中用到了Executor、Executors、 ExecutorService、 ThreadPoolExecutor这几个类
6.2.2创建线程池3种
-
Executors.newFixedThreadPool(int) 创建指定线程数量的线程池
-
Executors.newSingleThreadExecutor() 创建只有一个线程的线程池
-
Executors.newCachedThreadPool() 根据需要创建新的线程,可扩容
Executors.newFixedThreadPool(int)
public class MyThreadDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(5); //一池5个工作线程,类似一个银行有5个受理窗口
try {
//模拟有10个顾客过来银行办理业务,目前池子里面有5个工作人员提供服务
for (int i = 1; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" 办理业务");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-4 办理业务
pool-1-thread-2 办理业务
pool-1-thread-2 办理业务
pool-1-thread-5 办理业务
pool-1-thread-3 办理业务
pool-1-thread-4 办理业务
pool-1-thread-1 办理业务
注意:线程池的名字自己有
Executors.newSingleThreadExecutor()
public class MyThreadDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor(); //y一池1个工作线程,类似一个银行有1个受理窗口
try {
//模拟有10个顾客过来银行办理业务,目前池子里面有1个工作人员提供服务
for (int i = 1; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" 办理业务");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
Executors.newFixedThreadPool(int) 问题:假设创建了拥有5个线程的线程池,如果只有2个线程来访问则浪费
Executors.newSingleThreadExecutor() 问题:假设创建了拥有1个线程的线程池,如果有多个线程来访问则造成浪费
Executors.newCachedThreadPool()
可以解决上述问题,动态创建
Executors.newCachedThreadPool()
public class MyThreadDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool(); //一池N个工作线程,类似一个银行有N个受理窗口
try {
//模拟有10个顾客过来银行办理业务,目前池子里面有N个工作人员提供服务
for (int i = 1; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" 办理业务");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
pool-1-thread-2 办理业务
pool-1-thread-1 办理业务
pool-1-thread-3 办理业务
pool-1-thread-4 办理业务
pool-1-thread-5 办理业务
pool-1-thread-1 办理业务
pool-1-thread-3 办理业务
pool-1-thread-2 办理业务
pool-1-thread-4 办理业务
6.2.3ThreadPoolExecutor底层原理
6.3线程池7个重要的参数
- corePoolSize:线程池中的常驻核心线程数,线程池中会维护一个最小的线程数量,即使这些线程处于空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut
- maximumPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于等于1
- CPU 密集型程序:maximumPoolSize设置成核心数
Runtime.getRuntime().availableProcessors()获取核心数
- I/O 密集型程序:maximumPoolSize设置成CPU核心数的两倍最佳
- keepAliveTime:多余的空闲线程的存活时间,当前池中线程数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余线程会被销毁直到只剩下corePoolSize个线程为止
- unit :空闲线程存活时间单位 keepAliveTime的计量单位
- workQueue :任务队列,被提交但尚未被执行的任务
- threadFactory :线程工厂创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等
- handler :拒绝策略,表示当队列满了,并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时如何来拒绝请求执行的runnable的策略
解释:
假设一个银行网点常驻办理窗口3个(corePoolSize),如果突然很多顾客来办理业务,将无法及时办理业务的顾客让其在等候区排队(workQueue),如果等候区也满了,将窗口增加至5个(maximumPoolSize)。如果5个窗口满了而且等候区也满了,则关门拒绝提供服务(handler)。假如一段时间内(keepAliveTime)都只有少于3个顾客,此时3个窗口已经够用,将5个窗口减少至3个,也就是空闲线程失活。
6.4线程池底层工作原理
6.5线程池用哪个?生产中如何设置合理参数?
6.5.1线程池的拒绝策略
是什么?
等待队列已经排满了
,再也塞不下新任务了,同时线程池中的max线程也达到了
,无法继续为新任务服务,这是就需要拒绝策略机制合理的处理这个问题。
JDK内置的拒绝策略
-
AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行
-
CallerRunsPolicy:调用者运行,一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。
-
DiscardPolicy:该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常,如果允许任务的丢失,这是最好的一种策略。
-
DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中,尝试再次提交当前任务
6.5.2三种创建线程池的方法那个用的多?都不用
阿里巴巴开发手册
6.5.3自定义线程池
当使用AbortPolicy拒绝策略时
,如果线程数量超过maximumPoolSize+workQueue则报错
public class MyThreadDemo {
public static void main(String[] args) {
ThreadPoolExecutor threadPool= new ThreadPoolExecutor(2,5,2L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
try {
for (int i = 0; i < 9; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" 办理业务");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
当使用CallerRunsPolicy拒绝策略时
,如果线程数量超过maximumPoolSize+workQueue则回退给调用者运行
public class MyThreadDemo {
public static void main(String[] args) {
ThreadPoolExecutor threadPool= new ThreadPoolExecutor(2,5,2L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());
.....
省略,同上
}
}
当使用DiscardPolicy拒绝策略时
,如果线程数量超过maximumPoolSize+workQueue则丢弃无法处理的线程
public class MyThreadDemo {
public static void main(String[] args) {
ThreadPoolExecutor threadPool= new ThreadPoolExecutor(2,5,2L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());
.....
省略,同上
}
}
只运行了8个任务,有一个任务被抛弃
当使用DiscardOldestPolicy拒绝策略时
,如果线程数量超过maximumPoolSize+workQueue则抛弃队列中等待最久的任务
public class MyThreadDemo {
public static void main(String[] args) {
ThreadPoolExecutor threadPool= new ThreadPoolExecutor(2,5,2L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());
.....
省略,同上
}
}