欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JAVA实现多线程的四种方式和 ThreadPoolExecutor线程池内容详解 七大参数 拒绝策略 自定义线程池

程序员文章站 2022-05-04 17:54:00
...

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使用不同的参数实现
JAVA实现多线程的四种方式和 ThreadPoolExecutor线程池内容详解 七大参数 拒绝策略 自定义线程池

4.3 ThreadPoolExecutor 的七大参数分析

JAVA实现多线程的四种方式和 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(); 该策略默默的丢弃无法处理的任务,不予任何处理也不抛出异常。如果允许任务丢失,这是一个最好的策略。