06:线程池
1:线程池原理-基本概念: 1:线程池管理器:用户管理线程池。包括创建线程池、销毁线程池,添加新任务等。 2:工作线程:工作线程就是线程池中实际工作的线程。没有任务时:处于等待状态,有任务时:可以循环的执行任务。 3:任务接口:每个任务都需要实现的接口。规范了任务的输入、输出等。 4:任务队列:任务太多时,超过了线程池处理能力。将待处理的任务放到等待队列中。 2:线程池接口和实现类: 1:接口:Executor:最上层的接口:定义了执行任务的方法:executor() 2:接口:ExecutorService:继承了Executor接口,扩展了Callable,Future,关闭方法。 3:接口:ScheduledExecutorService:继承了ExecutorService,怎加了定时任务相关的方法。 4:实现类:ThreadPoolExecutor:标准的线程池实现。但是比较基础。 5:实现类:ScheduledThreadPoolExecutor:继承了ThreadPoolExecutor,实现了ScheduledExecutorService。也就是说:在标准的线程池类基础上怎加了定时任务。
3:ThreadPoolExecutorc测试:
/** * 1:核心线程数量5,最大数量10,无界队列,超出核心线程数量的线程存活时间5秒。 */ // 没有达到线程池最大线程数量10,而是进入了等待队列。 private static void threadPoolExecutorTest1() throws Exception{ 当前线程池线程数量为:5 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 当前线程池等待队列的长度:5 5, // 核心线程数量 线程结束:7 10, // 最大线程数量 ... 5, // 超出核心线程数量的线程的存活时间 线程结束:14 TimeUnit.SECONDS, // 单位 当前线程池线程数量为:5 new LinkedBlockingQueue<Runnable>()); // 线程等待队列 当前线程池等待队列的长度:0 testCommon(threadPoolExecutor); // 因为没有把队列占满,只有等待队列满了才会超出核心线程数量 } /** * 1:等待队列长度为3,2: 指定拒绝策略。 */ private static void threadPoolExecutorTest2() throws Exception{ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3), 等待队列调小为3 new RejectedExecutionHandler() { // 如果(等待队列满了 && 最大线程数量也达到了):执行拒接策略 @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println("任务被拒绝执行。"); } } ); testCommon(threadPoolExecutor); } /** * 1:核心线程数量 = 最大线程数 , 队列无界 */ private static void threadPoolExecutorTest3()throws Exception{ // 和线程池工具类的 Executors.newFixedThreadPool(int nThreads)实现一样。 // 和Executors.newFixedThreadPool(int nThreads)实现一样 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 5, 5, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>() ); testCommon(threadPoolExecutor); } /** * SynchronousQueue,实际上不是一个真正的队列,因为他不会为队列中的元素维护存储空间。他维护的是一组线程。 * 这种线程池起初没有线程,最后执行完成后也没有线程。 * 60秒后无用的线程就会销毁 */ private static void threadPoolExecutorTest4() throws Exception{ // 和Executors.newCachedThreadPool() 实现一样 // 和 Executors.newCachedThreadPool() 方法一样 // 当无法预估需要多少线程,但是最大线程数量自己设定。不然太大了。 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0,Integer.MAX_VALUE, 60L,TimeUnit.SECONDS, // 如果运行过程中有线程,就复用。没有就加开。 new SynchronousQueue<Runnable>()); // 不工作的线程,60秒后自动销毁。 } // 注意控制最大线程数量。 /** * 定时执行 : 3秒后执行,一次性执行,到时间就执行。 * 核心线程数:5 , 最大线程数为:Integer.MAX_VALUE * DelayedWorkQueue 为延时队列,存放进去一定时间后才可以取出来。 * @throws Exception */ private static void threadPoolExecutorTest5() throws Exception{ // 定时任务,延时执行一次。 // Executors.newScheduledThreadPool() ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(5); // 如果你的任务需要很多线程,就调多一点。 scheduledThreadPoolExecutor.schedule(new Runnable() { @Override public void run() { System.out.println("任务执行"); } }, 3000, TimeUnit.MICROSECONDS); // 3秒之后执行 } /** * 定时任务,周期性执行。(固定频率,固定延迟) * 固定线程数:5 核心线程数5 最大线程数Integer.MAX_VALUE 延时队列,超出核心线程数量的线程存活0秒。 * 例如:需要定时查询数据库的任务。 */ private static void threadPoolExecutorTest6() throws Exception{ // 周期性执行任务有两种调度方式。 ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5); // 第一种:固定频率:提交后:2秒后开始第一次执行,之后每间隔一秒,固定执行一次。(如果发现上次任务未完成,等待完毕后立即执行下一个任务) threadPoolExecutor.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("如果任务执行时间超出了周期时间,下一个任务直接开始"); } },2000 , 1000 ,TimeUnit.MICROSECONDS); // 第二种:固定延迟:提交后:两秒后开始第一次执行,之后每间隔一秒,固定执行一次。(如果发现上次任务未完毕,等上一次完毕后,再等待一秒。然后执行下一个任务) threadPoolExecutor.scheduleWithFixedDelay(new Runnable() { @Override public void run() { System.out.println("如果任务执行时间超出了周期时间,下一个任务重新计算执行延时后再执行"); } },2000,1000,TimeUnit.MICROSECONDS); } /** * 测试:提交15个执行时间为3秒的任务。并输出线程状态。 * @param threadPoolExecutor 显示池 */ private static void testCommon(ThreadPoolExecutor threadPoolExecutor)throws Exception{ for (int i = 0; i < 15; i++) { int n = i; threadPoolExecutor.submit(new Runnable() { @Override public void run() { try { System.out.println("开始时间:"+n); Thread.sleep(3000L); System.out.println("线程结束:"+n); } catch (InterruptedException e) { e.printStackTrace(); } } }); System.out.println("任务提交成功"+i); } // 等待5秒后看线程池状态 Thread.sleep(5000L); System.out.println("当前线程池线程数量为:"+threadPoolExecutor.getPoolSize()); System.out.println("当前线程池等待队列的长度:"+threadPoolExecutor.getQueue().size()); // 等待15秒,查看线程池状态(理论上,超出核心线程数量的线程会被销毁) Thread.sleep(10000L); System.out.println("当前线程池线程数量为:"+threadPoolExecutor.getPoolSize()); System.out.println("当前线程池等待队列的长度:"+threadPoolExecutor.getQueue().size()); }
4:可以用线程池工具类快速创建线程池:
5:如何合适的定义线程池的线程数量: 1:计算型任务(计算数据任务):线程的数量是CPU熟练的 1~2倍即可。 2:IO型任务(网络操作,数据库操作,文件操作):根据具体的IO阻塞时长考量。就是说阻塞的时候开多线程执行。 3:考虑设置最小的线程数量和最大线程数量。自动在这个区间怎加线程数量。 4:窍门:通过监控CPU的使用率。达到80%就说明cpu利用起来了。