Java学习笔记5-线程池
线程池原理 - 我的理解就是个送快递的网点
- 线程池管理器:用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务;- 快递网点管理者,可以招聘和辞退快递小哥,收件分件等
- 工作线程:线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;- 送快递的小哥
- 任务接口:每个任务必须实现的接口,以供工作线程调度任务的执行,他主要规定了任务的入口,任务执行完成后的收尾工作,任务的执行状态等;- 快递包裹的规格,快递单及配送信息等,没地址的快递小哥可没法送
- 任务队列:用于存放没有处理的任务。提供一种缓冲机制。- 存放快递包裹的仓库
线程池API
-
Executor(接口):最上层的接口,定义了执行任务的方法execute
-
ExecutorService(接口):继承Executor接口,扩展了Callable、Future、关闭方法
-
ScheduledExecutorService(接口):继承ExecutorService接口,增加了定时任务相关方法
-
ThreadPoolExecutor(实现类):基础、标准的线程池实现,推荐使用,创建线程达到核心线程数 -> 再将任务队列放满 -> 可创建线程达到最大线程数 -> 执行拒绝策略
public ThreadPoolExecutor(int corePoolSize, // 核心线程数量 int maximumPoolSize, // 最大线程数量 long keepAliveTime, // 超出核心线程数量的线程空闲存活时间 TimeUnit unit, // 存活时间单位 BlockingQueue<Runnable> workQueue, // 等待任务队列 ThreadFactory threadFactory, // 线程创建工厂,可选 RejectedExecutionHandler handler // 指定拒绝策略的,可选 ) { // ... }
ThreadPoolExecutor测试代码
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @Author: Wenx * @Description: * @Date: Created in 2019/11/11 17:14 * @Modified By: */ public class DemoTest { public static void main(String[] args) throws InterruptedException { // 创建一个 核心线程数量为2,最大线程数量为5,等待队列为3 的线程池,相当于最大容纳8个任务 // 默认的策略是抛出RejectedExecutionException异常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 5, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3), new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.err.println("快递网点:快递小哥不够仓库已满 / 停止业务 -> 拒收快递包裹"); } }); // 测试: 提交 10个 执行时间需要3秒的任务,看超过2个任务时的对应处理情况 int num = 10; for (int i = 0; i < num; i++) { int n = i + 1; System.out.println("快递包裹" + n + ":到达快递网点"); threadPoolExecutor.submit(new Runnable() { @Override public void run() { try { System.out.println("------------------------------快递小哥正在送 快递包裹" + n); Thread.sleep(3000L); System.out.println("------------------------------快递包裹" + n + " 已送达"); } catch (InterruptedException e) { System.err.println("------------------------------快递小哥翻车了:" + e.getMessage()); } } }); System.out.println("派件快递小哥人数为:" + threadPoolExecutor.getPoolSize()); System.out.println("网点仓库包裹数量为:" + threadPoolExecutor.getQueue().size()); } // 8秒后快递包裹应该都配送完了(具体延迟时间可以调整至都配送完,还有5人=2核心+3临时) Thread.sleep(8000L); System.out.println("快递网点:快递包裹应该差不多配送完了"); System.out.println("派件快递小哥人数为:" + threadPoolExecutor.getPoolSize()); System.out.println("网点仓库包裹数量为:" + threadPoolExecutor.getQueue().size()); // 没活干5秒的临时快递小哥要被辞退了(空闲5秒就要被辞退,就剩核心2人) Thread.sleep(5000L); System.out.println("快递网点:没活干5秒的临时快递小哥要被辞退"); System.out.println("派件快递小哥人数为:" + threadPoolExecutor.getPoolSize()); System.out.println("网点仓库包裹数量为:" + threadPoolExecutor.getQueue().size()); threadPoolExecutor.shutdown(); // 快递网点停止业务,但现有快递包裹还会配送 //threadPoolExecutor.shutdownNow(); // 快递网点停止业务,现有快递包裹也不会配送 System.out.println("快递网点:停止业务"); Thread.sleep(1000L); System.out.println("派件快递小哥人数为:" + threadPoolExecutor.getPoolSize()); System.out.println("网点仓库包裹数量为:" + threadPoolExecutor.getQueue().size()); // 再次提交提示失败 System.out.println("快递包裹" + (num + 1) + ":到达快递网点"); threadPoolExecutor.submit(new Runnable() { @Override public void run() { System.out.println("又来一个快递包裹"); } }); // 结果分析 // 1、 2个任务(快递包裹)直接分配线程(快递小哥)开始执行(配送) // 2、 3个任务(快递包裹)进入等待队列(快递网点仓库) // 3、 队列(仓库)不够用,临时加开3个线程(快递小哥)来执行任务(5秒没活干就辞退) // 4、 队列(仓库)和线程池(快递小哥)都满了,剩下2个任务(快递包裹),没资源了,被拒绝执行(拒收)。 // 5、 等待现有任务执行结束(配送完),应当还有5个任务(5人=2核心+3临时) // 6、 5秒后,如果无任务可执行(没活干),销毁临时创建的3个线程(辞退临时快递小哥) // 7、 调用shutdown(停止业务),不接收新的任务(快递包裹) // 8、 在线程池关闭(停止业务)后追加的任务(快递包裹),无法再提交,会被拒绝执行(拒收) } }
-
ScheduledThreadPoolExecutor(实现类):定时任务线程池实现,继承了ThreadPoolExecutor,实现了ScheduledExecutorService中定时任务相关的方法
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, // 核心线程数量 Integer.MAX_VALUE, // 最大线程数量 0, // 超出核心线程数量的线程空闲存活时间 NANOSECONDS, // 存活时间单位 new DelayedWorkQueue() // 等待任务队列-延时队列,没有到达时间取不出来 ); }
ScheduledThreadPoolExecutor测试代码
import java.text.SimpleDateFormat; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @Author: Wenx * @Description: * @Date: Created in 2019/11/11 17:14 * @Modified By: */ public class DemoTest { public static void main(String[] args) throws InterruptedException { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5); // 定时3秒后执行一次任务 threadPoolExecutor.schedule(new Runnable() { @Override public void run() { System.out.println("定时任务1 开始执行,当前时间为:" + dateFormat.format(System.currentTimeMillis())); } }, 3000, TimeUnit.MILLISECONDS); System.out.println("定时任务1 提交成功,当前时间为:" + dateFormat.format(System.currentTimeMillis())); System.out.println("当前线程池中线程数量:" + threadPoolExecutor.getPoolSize()); // 周期性执行任务 // scheduleAtFixedRate:2秒后执行第一次任务,之后每间隔1秒执行一次(如果上次执行还未结束,则等待结束后立刻执行) threadPoolExecutor.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("定时任务2 开始执行,当前时间为:" + dateFormat.format(System.currentTimeMillis())); } }, 2000, 1000, TimeUnit.MILLISECONDS); System.out.println("定时任务2 提交成功,当前时间为:" + dateFormat.format(System.currentTimeMillis())); System.out.println("当前线程池中线程数量:" + threadPoolExecutor.getPoolSize()); // scheduleWithFixedDelay:2秒后执行第一次任务,之后每间隔1秒执行一次(如果上次执行还未结束,则等待结束后,再等待1秒执行) threadPoolExecutor.scheduleWithFixedDelay(new Runnable() { @Override public void run() { try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("定时任务3 开始执行,当前时间为:" + dateFormat.format(System.currentTimeMillis())); } }, 2000, 1000, TimeUnit.MILLISECONDS); System.out.println("定时任务3 提交成功,当前时间为:" + dateFormat.format(System.currentTimeMillis())); System.out.println("当前线程池中线程数量:" + threadPoolExecutor.getPoolSize()); } }
-
Executors(工具类):创建线程池工厂类,不推荐使用,因为创建的都是无界队列线程池,线程处理不完的话,队列有多少收多少,推荐直接使用ThreadPoolExecutor配置有界队列
newFixedThreadPool(int nThreads):创建一个固定大小的线程池
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
newCachedThreadPool():创建一个缓冲线程池,可以动态调整线程池大小
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
newSingleThreadExecutor():创建一个单一线程池
public static ExecutorService newSingleThreadExecutor() { return new Executors.FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
newScheduledThreadPool(int corePoolSize):创建一个定时执行任务的线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }