认识线程池
-
什么是线程池?
线程池就是一个可以复用线程的技术
-
不使用线程池的问题
用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的,而创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。
-
为什么线程池可以提高系统的工作性能
因为线程池可以控制线程和任务的数量,因此不会因为线程或者任务过多而导致系统瘫痪
-
线程池工作原理图
如何创建线程池
代表线程池的接口ExecutorService
接口是无法直接创建对象的,因此要用到他的实现类,常用创建对象来代表一个具体线程池
如何得到线程池对象
ThreadPoolExecutor
构造器七个参数(面试重点
核心线程数量+临时线程数量=最大线程数量
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
以餐厅来举例理解
ctrl+鼠标左键点击ThreadPoolExecutor选择参数最多的一个
LinkedBlockingQueue<>()
不限制大小
ArrayBlockingQueue<>(4)
自己设置大小
public static void main(String[] args) {//1.通过ThreadPoolExecutor创建一个线程池对象ExecutorService pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS,new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());}
线程池注意事项(面试重点)
0. 线程池分层的任务调度策略
线程池的核心目标是平衡资源利用和任务处理效率 ,同时避免过度消耗系统资源(如CPU、内存)。为了实现这一目标,线程池采用了分层的任务调度策略:
- 优先使用核心线程 :核心线程是线程池的“主力”,负责处理大部分任务。
- 任务积压时使用任务队列 :当核心线程都在忙时,新任务会被放入任务队列中等待执行,而不是立即创建新线程。
- 最后才创建临时线程 :只有在任务队列已满且核心线程都在忙的情况下,才会创建临时线程。
这种分层策略的设计目的是尽量减少线程的创建和销毁开销 ,因为线程的创建和销毁是非常昂贵的操作。
总结:线程池的任务调度优先级是:核心线程 →任务队列 →临时线程
1.临时线程什么时候创建
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
2.什么时候会开始拒绝新任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务
线程池处理Runnable任务
ExecutorService的常用方法
execute()
相关代码:
public class MyRunnable implements Runnable{@Overridepublic void run() {//描述任务是干什么的System.out.println(Thread.currentThread().getName()+"Wolves487");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}
}
/*线程池的创建*/
public class ThreadPoolTest1 {public static void main(String[] args) {//1.通过ThreadPoolExecutor创建一个线程池对象ExecutorService pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS,new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());Runnable target = new MyRunnable();pool.execute(target); //线程池会自动创建一个新线程,自动处理这个任务,自动执行的pool.execute(target); //线程池会自动创建一个新线程,自动处理这个任务,自动执行的pool.execute(target); //线程池会自动创建一个新线程,自动处理这个任务,自动执行的pool.execute(target); //复用前面的核心线程pool.execute(target); //复用前面的核心线程}
}
.shutdown() .shutdownNow()
线程池启动后是不会自动关闭的,因为在开发中本来就要保证线程池不死亡
pool.shutdown(); //等线程池的所有任务全部执行完后,再关闭线程池
pool.shutdownNow(); //立即关闭线程池,不管任务是否执行完(可能出现异常
实际开发中一般不关闭线程池
核心线程在忙,任务队列占满了的时候,并且还可以创建临时线程,等新任务过来时就会创建临时线程
让每个任务执行线程的时候休眠时间无限大,就可以保证核心线程一直忙了
public class MyRunnable implements Runnable{@Overridepublic void run() {//描述任务是干什么的System.out.println(Thread.currentThread().getName()+"Wolves487");try {Thread.sleep(Integer.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}}
}
case1:未创建临时线程
case2:创建了临时线程
case3:达到最大线程数,触发拒绝策略
/*线程池的创建*/
public class ThreadPoolTest1 {public static void main(String[] args) {//1.通过ThreadPoolExecutor创建一个线程池对象ExecutorService pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS,new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());Runnable target = new MyRunnable();pool.execute(target); //线程池会自动创建一个新线程,自动处理这个任务,自动执行的pool.execute(target); //线程池会自动创建一个新线程,自动处理这个任务,自动执行的pool.execute(target); //线程池会自动创建一个新线程,自动处理这个任务,自动执行的pool.execute(target); //进入任务队列pool.execute(target); //进入任务队列pool.execute(target); //进入任务队列pool.execute(target); //进入任务队列//到临时线程创建的时机了pool.execute(target);pool.execute(target);// 3+2=5 到新任务的拒绝时机了pool.execute(target);}
}
补充:新任务拒绝策略
线程池处理Callable任务
ExecutorService的常用方法
/*线程池的创建*/
public class ThreadPoolTest2 {public static void main(String[] args) throws ExecutionException, InterruptedException {//1.通过ThreadPoolExecutor创建一个线程池对象ExecutorService pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS,new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());//2、使用线程处理Callable任务Future<String> f1 = pool.submit(new MyCallable(100));Future<String> f2 = pool.submit(new MyCallable(200));Future<String> f3 = pool.submit(new MyCallable(300));Future<String> f4 = pool.submit(new MyCallable(400));System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());}
}
/*** 1、实现Callable接口*/
public class MyCallable implements Callable<String> {private int n;public MyCallable(int n) {this.n = n;}//2、重写call方法@Overridepublic String call() throws Exception {//描述线程的任务,返回线程执行返回后的结果//需求:求1~n的和并返回int sum = 0;for (int i = 0; i <= n; i++) {sum += i;}return Thread.currentThread().getName()+":n"+"的和为"+sum;}
}
Executors工具类实现线程池
Executors是一个线程池的工具类,提供了很多静态方法,用于返回不同特点的线程池对象
//1.通过Executors创建一个线程池对象ExecutorService pool = Executors.newFixedThreadPool(3);//新建固定线程数的线程池ExecutorService pool1 = Executors.newSingleThreadExecutor();//新建1个线程数的线程池
注意:这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象
核心线程数量到底配置多少
-
计算密集型任务
核心线程数量 = CPU的核数+1
-
IO密集型任务
核心线程数量 = CPU的核数+2
Executors使用可能存在的陷阱(注意)
大型并发系统环境中(如淘宝、京东)使用Executors如果不注意可能会出现系统风险
如阿里巴巴开发手册中写到