JAVA实现多线程的四种方式和 ThreadPoolExecutor线程池内容详解 七大参数 拒绝策略 自定义线程池
JAVA多线程的四种方式 线程池详细解析
JAVA多线程四种方式
java中实现线程的4种方式:
- 1.继承Thread类 重写run方法
- 2.实现Runnable接口 实现run方法
- 3.继承Callable接口 实现call方法 使用futureTask调用 (有返回值/可处理异常)
[以上三种在平常业务代码种均不使用.]
[应该将所有的多线程异步任务都交给线程池执行]
-
4.线程池(线程复用;控制最大并发数;管理线程)
-
降低资源的消耗
- 通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
-
提高响应速度
- 因为线程池中的线程数没有超过最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
-
提高线程的可管理性
- 线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销.无线的创建和销毁线程不仅小号系统资源,还降低系统的稳定性,使用线程池进行统一分配
-
1.继承Thread类 重写run方法
public class ThreadTest {
public static void main(String args[]){
System.out.println("main start ....");
// 1.继承Thread类 重写run方法
new Thread01().start();
System.out.println("main end ....");
}
public static class Thread01 extends Thread{
@Override
public void run() {
System.out.println("1.继承Thread类 重写run方法");
}
}
}
2.实现Runnable接口 实现run方法
public class ThreadTest {
public static void main(String args[]){
System.out.println("main start ....");
// 2.实现Runnable接口 实现run方法
new Thread(new Runnable01()).start();
System.out.println("main end ....");
}
public static class Runnable01 implements Runnable{
@Override
public void run() {
System.out.println("2.实现Runnable接口 实现run方法");
}
}
}
3.继承Callable接口 实现call方法 使用futureTask调用 (有返回值/可处理异常)
public class ThreadTest {
public static void main(String args[]) throws ExecutionException, InterruptedException {
System.out.println("main start ....");
// 3.继承Callable接口 实现call方法 使用futureTask调用 (有返回值/可处理异常)
Callable01 callable01 = new Callable01();
FutureTask<Integer> futureTask = new FutureTask<>(callable01);
new Thread(futureTask).start();
//使用futureTask 获取返回值 会阻塞等待
System.out.println(futureTask.get());
System.out.println("main end ....");
}
public static class Callable01 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("3.继承Callable接口 实现call方法 使用futureTask调用 (有返回值/可处理异常)");
return new Integer(200);
}
}
}
4.线程池
4.1基础线程池的应用
public class ThreadTest {
//正常情况保证一个项目中只有少数线程池,每个异步任务,线程池让他自己取执行
//Executors.newFixedThreadPool(10); 使用工具类生成容量为10的线程池
//自定义线程池使用 new ThreadPoolExecutor(); 自定义七大参数
public static ExecutorService service = Executors.newFixedThreadPool(10);
public static void main(String args[]){
System.out.println("main start ....");
try{
service.execute(()->{
System.out.println("4.线程池");
});
}catch (Exception e){
e.printStackTrace();
}finally{
service.shutdown();
}
System.out.println("main end ....");
}
}
4.2 Executors 辅助工具类
Executors.newFixedThreadPool(int); //创建固定容量的线程池
Executors.newSingleThreadExecutor(); //创建一个只有1个工作线程的线程池
Executors.newCachedThreadPool() //创建一个可扩容的线程池。执行很多短期异步任务,线程池根据需要创建新线程,但在先前构造的线程可用时将重用他们。可扩容。
public class MyThreadPoolDemo {
public static void main(String args[]){
//固定容量的线程池
// ExecutorService threadPool =Executors.newFixedThreadPool(5); //创建一个有5个工作线程的线程池
// ExecutorService threadPool =Executors.newSingleThreadExecutor(); //创建一个只有1个工作线程的线程池
ExecutorService threadPool =Executors.newCachedThreadPool(); //创建一个可扩容的线程池
try{
for (int i = 1; i <10; i++) {
//从线程池中使用一个工作线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
}catch (Exception e){
}finally{
//任务结束 归还线程
threadPool.shutdown();
}
}
}
以上三个Executors API底层均使用ThreadPoolExecutor使用不同的参数实现
4.3 ThreadPoolExecutor 的七大参数分析
-
1、corePoolSize:线程中的常驻核心线程数
-
2、maxmunPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于1
-
3、keepAliveTime:多余空闲线程的存活时间。当前池中线程数量超过corePoolSize时&&当空闲时间达到keepAliveTime时,多余线程会被销毁直到剩下corePoolSize个线程为止(过剩策略)
-
4、unit:keepAliveTime的单位
-
5、workQueue:任务阻塞队列,被提交但尚未被执行的任务(不够策略)
-
6、threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程。一般默认即可
-
7、handler:拒绝策略,表示当任务阻塞队列满了,并且共工作线程大于等于线程池的最大线程数(maxmunPoolSize)时,如何拒绝请求执行的Runnable策略(饱满策略)
4.4 ThreadPoolExecutor工作原理
-
1、在创建了线程池后,开始等待请求。
-
2、当调用execute()方法添加一个请求任务时,线程池会做出如下判断:
2.1如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;
2.2如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;
2.3如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
2.4如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
-
3、当一个线程完成任务时,它会从队列中取下一个任务来执行。
-
4、当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:
如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。
所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。
4.5 自定义线程池
使用7大参数自定义线程池(最主要的参数是拒绝策略)
public class MyThreadPoolDemo {
public static void main(String args[]){
ExecutorService threadPool = new ThreadPoolExecutor(2,
5,
2L,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
}
四大拒绝策略:
1、ThreadPoolExecutor.AbortPolicy(); 直接抛出RejectExecutionException 异常阻止系统正常运行
2、ThreadPoolExecutor.CallerRunsPolicy(); 调用者运行 一中调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量
3、ThreadPoolExecutor.DiscardOldestPolicy(); 抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务
4、ThreadPoolExecutor.DiscardPolicy(); 该策略默默的丢弃无法处理的任务,不予任何处理也不抛出异常。如果允许任务丢失,这是一个最好的策略。
机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量
3、ThreadPoolExecutor.DiscardOldestPolicy(); 抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务
4、ThreadPoolExecutor.DiscardPolicy(); 该策略默默的丢弃无法处理的任务,不予任何处理也不抛出异常。如果允许任务丢失,这是一个最好的策略。
上一篇: Java 多线程学习总结和笔记