线程池、默认线程池、指定上限的线程池、自定义线程池、四种任务策略
活动地址:CSDN21天学习挑战赛
线程状态 | 描述 | 具体含义 |
---|---|---|
新建状态(NEW) | 创建线程对象 | 一个尚未启动的线程的状态。也称之为初始状态、开始状态。线程刚被创建,但是并未启动。还没调用start方法。MyThread t = new MyThread()只有线程象,没有线程特征。 |
就绪状态/可运行状态(RUNNABLE) | start方法 | 当我们调用线程对象的start方法,那么此时线程对象进入了RUNNABLE状态。那么此时才是真正的在JVM进程中创建了一个线程,线程一经启动并不是立即得到执行,线程的运行与否要听令与CPU的调度,那么我们把这个中间状态称之为可执行状态(RUNNABLE)也就是说它具备执行的资格,但是并没有真正的执行起来而是在等待CPU的度。 |
阻塞状态 (BLOCKED) |
无法获得锁对象 | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。 |
等待状态(WAITING) | wait方法 | 一个正在等待的线程的状态。也称之为等待状态。造成线程等待的原因有两种,分别是调用Object.wait()、join()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。 |
计时等待 (TIMED_WAITING) |
sleep方法 | 一个在限定时间内等待的线程的状态。也称之为限时等待状态。造成线程限时等待状态的原因有三种,分别是:Thread.sleep(long),Object.wait(long)、join(long)。 |
结束状态 (TERMINATED) |
全部代码运行完毕 | 一个完全运行完成的线程的状态。也称之为终止状态、结束状态 |
概述
- 线程池也是可以看做成一个池子,在该池子中存储很多个线程。
- 线程池就是一个可以复用线程的技术。
线程池存在的意义:
- 系统创建一个线程的成本是比较高的,因为它涉及到与操作系统交互,当程序中需要创建大量生存期很短暂的线程时,频繁的创建和销毁线程对系统的资源消耗有可能大于业务处理是对系统资源的消耗,这样就有点"舍本逐末"了。针对这一种情况,为了提高性能,我们就可以采用线程池。
- 线程池在启动的时,会创建大量空闲线程,当我们向线程池提交任务的时,线程池就会启动一个线程来执行该任务。等待任务执行完毕以后,线程并不会死亡,而是再次返回到线程池中称为空闲状态。等待下一次任务的执行。
线程池的设计思路
准备一个任务容器
一次性启动多个(2个)消费者线程
刚开始任务容器是空的,所以线程都在wait
直到一个外部线程向这个任务容器中扔了一个"任务",就会有一个消费者线程被唤醒
这个消费者线程取出"任务",并且执行这个任务,执行完毕后,继续等待下一次任务的到来
概述
- JDK对线程池也进行了相关的实现,在真实企业开发中我们也很少去自定义线程池,而是使用JDK中自带的线程池。
Executors中所提供的静态方法来创建线程池
方法 描述 static ExecutorService newCachedThreadPool() 创建一个默认的线程池 static newFixedThreadPool(int nThreads) 创建一个指定最多线程数量的线程池 线程池-Executors默认线程池
代码实现
package com.moming9; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MyThreadPoolDemo { public static void main(String[] args) { //1.创建线程池 ExecutorService es = Executors.newCachedThreadPool(); //池子会自动帮我们创建线程对象,任务执行完毕, //有任务需要执行时,才会创建线程对象。 //当任务执行完毕,线程对象归还给池子。 es.submit(()-> System.out.println(Thread.currentThread().getName())); es.submit(()-> System.out.println(Thread.currentThread().getName())); //关闭线程池 es.shutdown(); } }
pool-1-thread-1 pool-1-thread-2
练习使用线程池执行带返回值的任务
此方法可以替换实现多线程的第三种方法(推荐使用)
package com.moming9; import java.util.concurrent.*; /** * 练习使用线程池执行带返回值的任务 */ public class MyTest { public static void main(String[] args) { //1.获取线程池对象 ExecutorService es = Executors.newCachedThreadPool(); //2.创建任务对象 MyCall myCall = new MyCall(3, 4); //3.将任务提交给线程池,就可以得到线程执行的结果了 Future
res = es.submit(myCall); //4.面向Future类型的对象res,调用get方法即可获取真正的结果了 Integer integer = null; try { integer = res.get();//try---catch } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println(integer); //5.关闭线程池 es.shutdown(); } } //任务类 class MyCall implements Callable { //利用构造方法接受参数 private int a ; private int b ; //创建构造方法 public MyCall(int a, int b) { this.a = a; this.b = b; } //方法重写 @Override public Integer call() throws Exception { return a+b; } } 7
线程池-Executors创建指定上限的线程池
package com.moming9; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public class MyThreadPoolDemo { public static void main(String[] args) { //参数不是初始值而是最大值 ExecutorService es = Executors.newFixedThreadPool(10); //线程池的实现类 ThreadPoolExecutor pool = (ThreadPoolExecutor) es; //获取核心线程数 System.out.println(pool.getPoolSize()); es.submit(()-> System.out.println(Thread.currentThread().getName())); es.submit(()-> System.out.println(Thread.currentThread().getName())); System.out.println(pool.getPoolSize()); es.shutdown(); } }
0 pool-1-thread-1 2 pool-1-thread-2
线程池-ThreadPoolExecutor
构造方法
方法 描述 ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) 用给定的初始参数创建一个新的ThreadPoolExecutor 创建线程池对象 :
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
代码实现 :
package com.moming9; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; //自己创建线程池 public class MyThreadPoolDemo4 { //参数一:核心线程数量 //参数二:最大线程数---大于等于核心线程数 //参数三:空闲线程最大存货时间 //参数四:时间单位---TimeUtil //参数五:任务队列---让任务在队列中等着,等有线程空闲了,在从这个队列中获取任务并执行 //参数六:创建线程工厂---按照默认的方式创建线程对象 //参数六:任务的拒绝策略---① 什么时候拒绝任务:当提交的任务 > 池子最大线程数量+任务队列的容量 // ②如何拒绝:四种拒绝策略:new ThreadPoolExecutor.AbortPolicy(),丢弃1多余的并抛出异常 //new ThreadPoolExecutor.DiscardPolicy,丢弃任务,但不抛出异常 //new ThreadPoolExecutor.DiscardOldestPolicy,抛弃队列中等待最久的任务,然后把当前任务加入队列中 //new ThreadPoolExecutor.CallerRunsPolicy,调用任务的run()方法绕过线程池直接执行 public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,5,2, TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()); for (int i = 1;i<16;i++) { threadPoolExecutor.submit(()->{ System.out.println(Thread.currentThread().getName()+"线程执行了"); }); } threadPoolExecutor.shutdown(); } }
pool-1-thread-5线程执行了 pool-1-thread-2线程执行了 pool-1-thread-3线程执行了 pool-1-thread-5线程执行了 pool-1-thread-5线程执行了 pool-1-thread-5线程执行了 pool-1-thread-3线程执行了 pool-1-thread-3线程执行了 pool-1-thread-3线程执行了 pool-1-thread-2线程执行了 pool-1-thread-3线程执行了 pool-1-thread-2线程执行了 pool-1-thread-5线程执行了 pool-1-thread-4线程执行了 pool-1-thread-1线程执行了
注意
- corePoolSize:核心线程的最大值,不能小于0
- maximumPoolSize:最大线程数,不能小于等于0,maximumPoolSize >= corePoolSize
- keepAliveTime:空闲线程最大存活时间,不能小于0
- unit:时间单位
- workQueue:任务队列,不能为null
- threadFactory:创建线程工厂,不能为null
- handler:任务的拒绝策略,不能为null
- 明确线程池最多可执行的任务数 = 队列容量 + 最大线程数
案例演示1:演示ThreadPoolExecutor.AbortPolicy任务处理策略
package com.moming9; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorDemo01 { public static void main(String[] args) { /** * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s */ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS , new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.AbortPolicy()) ; // 提交5个任务,而该线程池最多可以处理4个任务,当我们使用AbortPolicy这个任务处理策略的时候,就会抛出异常 for(int x = 0 ; x < 5 ; x++) { threadPoolExecutor.submit(() -> { System.out.println(Thread.currentThread().getName() + "---->> 执行了任务"); }); } } }
控制台报错,仅仅执行了4个任务,有一个任务被丢弃了
案例演示2:演示ThreadPoolExecutor.DiscardPolicy任务处理策略
package com.moming9; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorDemo02 { public static void main(String[] args) { /** * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s */ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS , new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardPolicy()) ; // 提交5个任务,而该线程池最多可以处理4个任务,当我们使用DiscardPolicy这个任务处理策略的时候,控制台不会报错 for(int x = 0 ; x < 5 ; x++) { threadPoolExecutor.submit(() -> { System.out.println(Thread.currentThread().getName() + "---->> 执行了任务"); }); } } }
控制台没有报错,仅仅执行了4个任务,有一个任务被丢弃了
案例演示3:演示ThreadPoolExecutor.DiscardOldestPolicy任务处理策略
package com.moming9; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorDemo03 { public static void main(String[] args) { /** * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s */ ThreadPoolExecutor threadPoolExecutor; threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS , new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardOldestPolicy()); // 提交5个任务 for(int x = 0 ; x < 5 ; x++) { // 定义一个变量,来指定指定当前执行的任务;这个变量需要被final修饰 final int y = x ; threadPoolExecutor.submit(() -> { System.out.println(Thread.currentThread().getName() + "---->> 执行了任务" + y); }); } } }
由于任务1在线程池中等待时间最长,因此任务1被丢弃。
案例演示4:演示ThreadPoolExecutor.CallerRunsPolicy任务处理策略
package com.moming4.moming9; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorDemo04 { public static void main(String[] args) { /** * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s */ ThreadPoolExecutor threadPoolExecutor; threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS , new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.CallerRunsPolicy()); // 提交5个任务 for(int x = 0 ; x < 5 ; x++) { threadPoolExecutor.submit(() -> { System.out.println(Thread.currentThread().getName() + "---->> 执行了任务"); }); } } }
通过控制台的输出,我们可以看到次策略没有通过线程池中的线程执行任务,而是直接调用任务的run()方法绕过线程池直接执行。