文章目录
- 线程池
- newSingleThreadExecutor
- newFixedThreadPool
- newCachedThreadPool
- newScheduledThreadPool
- 自定义线程池
线程池
线程池就是容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象、销毁线程的操作,避免系统资源的浪费。
线程池的优势?线程复用: 通过线程复用降低线程的创建和销毁造成的消耗提升响应速度: 当任务到达时,任务可以不需要等到线程创建就可以执行。提高线程的可管理性: 控制线程的并发数量,统一分配、管理线程常用的线程池
1、Executors.newSingleThreadExecutor(int) 任务按序执行 LinkedBlockingQueue
2、Executors.newFixedThreadPool(int) 执行长期的任务 LinkedBlockingQueue
3、Executors.newCachedThreadPool(int) 执行短期异步的小程序 SynchronousQueue
4、Executors.newScheduledThreadPool(int) 延迟重复执行线程
newSingleThreadExecutor
定义:单线程化线程池(只有一个线程),单线程化的线程池中的任务是按照提交的次序顺序执行的,唯一线程的存活时间是无限的,当池中的唯一线程正繁忙时,新提交的任务实例会进入内部的阻塞队列中,并且其阻塞队列是无界的
适用场景:任务按照提交次序,一个任务一个任务地逐个执行的场景
案例:
public class MyRunnable implements Runnable {private String taskName;public MyRunnable(String taskName) {this.taskName = taskName;}public void run() {System.out.println("task:" + taskName + " is doing...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("task:" + taskName + " end...");}
}public class Demo01_SingleThreadExecutor {public static void main(String[] args) {//1、获取线程池对象ExecutorService es = Executors.newSingleThreadExecutor();//2、提交任务for (int i = 1; i < 11; i++) {es.submit(new MyRunnable("任务" + i));}//3、关闭线程//es.shutdown();//关闭线程池,不接受新的任务,原来的任务继续执行//es.submit(new MyRunnable(88));//报错,不接受新任务// List<Runnable> al = es.shutdownNow();//立刻关闭线程池,若线程池还有缓存的任务,立即取消,并返回
// System.out.println(al);// es.submit(new MyRunnable(99));//报错,不接受新任务}
}
newFixedThreadPool
定义:固定数量的线程池,如果线程数没有达到“固定数量”,每次提交一个任务线程池内就创建一个新线程,直到线程达到线程池固定的数量,线程池的大小一旦达到“固定数量”就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程
适用场景:
- 需要任务长期执行的场景
- CPU密集型任务
缺点:
- 内部使用无界队列来存放排队任务,当大量任务超过线程池最大容量需要处理时,队列无限增大,使服务器资源迅速耗尽
案例:
public class demo26Thread06 {public static void main(String[] args) {//1、获取线程池对象ExecutorService es = Executors.newFixedThreadPool(3);//2、提交任务for (int i = 1; i < 11; i++) {es.submit(new MyRunnable("任务" + i));}}
}
newCachedThreadPool
特点:
1、可缓存线程池在接收新的异步任务target执行目标实例时,如果池内所有线程繁忙,此线程池就会添加新线程来处理任务
2、线程池不会对线程池大小进行限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小
3、如果部分线程空闲,也就是存量线程的数量超过了处理任务数量,就会回收空闲(60秒不执行任务)线程
适用场景:
需要快速处理突发性强、耗时较短的任务场景,如Netty的NIO处理场景、REST API接口的瞬时削峰场景
缺点:
线程池没有最大线程数量限制,如果大量的异步任务执行目标实例同时提交,可能会因创建线程过多而导致资源耗尽
案例:
public class Demo03_CachedThreadPool {public static void main(String[] args) {//1、获取线程池对象ExecutorService threadPool = Executors.newCachedThreadPool();//3、提交任务try {for (int i = 1; i < 11; i++) {threadPool.submit(new MyRunnable("任务" + i));}} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();//关闭线程池}}
}
newScheduledThreadPool
- 延时性
- 周期性
案例:
public class demo28Thread08 {public static void main(String[] args) {test01();// test02();间隔时间不同
// test03();}/*** 1、延迟2s执行* es.schedule(new MyRunnable("任务"+i),2, TimeUnit.SECONDS);*/private static void test01() {//1、获得延迟线程池对象ScheduledExecutorService es = Executors.newScheduledThreadPool(3);//2、编写任务类//3、提交多个任务for (int i = 1; i < 11; i++) {es.schedule(new MyRunnable("任务" + i), 2, TimeUnit.SECONDS);}}/*** 2、间隔时间不同* es.scheduleAtFixedRate(new MyRunnable("任务"+i),1,2,TimeUnit.SECONDS)*/private static void test02() {//1、获得延迟线程池对象ScheduledExecutorService es = Executors.newScheduledThreadPool(3, new ThreadFactory() {int n = 1;public Thread newThread(Runnable r) {return new Thread(r, "自定义线程池名称" + n++);}});//2、编写任务类//3、提交多个任务for (int i = 1; i < 11; i++) {es.scheduleAtFixedRate(new MyRunnable("任务" + i), 1, 2, TimeUnit.SECONDS);}System.out.println("执行完成");}/*** es.scheduleWithFixedDelay(new MyRunnable("任务"+i),1,2,TimeUnit.SECONDS)*/private static void test03() {//1、获得延迟线程池对象ScheduledExecutorService es = Executors.newScheduledThreadPool(3, new ThreadFactory() {int n = 1;public Thread newThread(Runnable r) {return new Thread(r, "自定义线程池名称" + n++);}});//2、编写任务类//3、提交多个任务for (int i = 1; i < 11; i++) {es.scheduleWithFixedDelay(new MyRunnable("任务" + i), 1, 2, TimeUnit.SECONDS);}System.out.println("执行完成");}
}
自定义线程池
自定义线程池ThreadPoolExecutor【七大参数】
1:核心线程数(corePoolSize): 线程池中常驻核心线程数
2:最大线程数(maximumPoolSize): 线程池能够容纳同时执行的最大线程数
3:keepAliveTime: 非核心线程最大存活时间,若当前线程池数量超过核心线程数corePoolSize,当空闲时间达到keepAliveTime,多余非核心线程会被销毁直到只剩下corePoolSize个线程。
4:unit: keepAliveTime的单位
5:任务队列(workQueue): 被提交但尚未执行的任务,存放在阻塞队列中
6:线程工厂(threadFactory): 用于创建线程,一般默认即可。
7:拒绝策略(handler): 表示当等待队列满了并且工作线程大于等于线程池的最大线程数常见拒绝策略:AbortPolicy(默认): 直接抛出异常RejectedExecutionException,阻止系统正常运行CallerRunsPolicy: 调用者优先策略,该策略不会抛弃任务,也不会抛出异常,而是将某些任务回退DiscardOldestPolicy: 抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务DiscardPolicy: 直接丢弃任务,不做任何处理也不抛出异常。如果允许任务丢失,可以使用
底层原理
1、在创建了线程池之后,等待提交过来的任务请求
2、当调用execute()方法后添加一个请求任务2.1 如果正在运行的线程数量小于corePoolSize,则立即执行该任务2.2 如果正在运行的线程数量大于等于corePoolSize,则将该任务放进队列 workQueue2.3 如果任务队列满了,并且正在运行的线程数量小于maximumPoolSize,那么线程池创建非核心线程执行任务2.4 如果任务队列满了,并且正在运行的线程数量大于等于maximumPoolSize,那么线程池启动拒绝策略执行。
3、当一个线程完成任务时,它会从队列取下一个任务执行
4、当一个线程无事可做超过一定的时间keepAliveTime,线程池会判断4.1 如果运行的线程数大于corePoolSize,那么会停掉该非核心线程4.2 最后线程池的线程数目收缩到corePoolSize的大小