zoukankan      html  css  js  c++  java
  • 并发编程004 --- 线程池的使用

    什么是线程池

    线程池是一种多线程的实现形式,处理过程中,将任务添加到队列,在创建线程后自动执行,线程池中的线程使用默认优先级(5)执行

    为什么使用线程池

    在实际应用开发中,我们很少直接使用Thread类来创建线程,因为大量的创建和销毁线程会带来很大的系统开销

    而线程池会保留核心线程,释放不常用线程,从而达到减小系统开销的效果。

    线程池的创建

    Exectors类提供了创建常用线程池的简便方法,有如下几种

    newSingleThreadPoolExecutor 创建只有一个线程的线程池
    newFixedThreadPoolExecutor 创建固定大小的线程池,入参指定
    newCachedThreadPoolExecutor 创建一个大小不做限定的线程池,支持的最大线程数为Integer最大值
    newScheduleThreadPoolExecutor 创建周期性执行的线程池,入参指定核心线程数,最大线程数为Integer最大值

    从源码来看,这些线程池的创建无一例外调用了ThreadPoolExecutor类的构造方法

     ThreadPoolExecutor类的构造器支持7个属性

    corePoolSize 线程池核心线程数,通常核心线程不会被销毁;如果指定allowCoreThreadTimeout为true,核心线程空闲超过keepAliveTime后,会被销毁;通常不建议这么做
    maximumPoolSize 线程池最大线程数
    keepAliveTime 线程空闲多长时间后,被销毁,一般用于非核心线程
    unit 超时时间单位
    workQueue 任务等待队列;如果线程池中线程个数达到最大值,并且无空闲线程,此时提交新的任务,该任务会被缓存到等待队列,直到有线程空闲
    threadFactory 线程池创建新的线程使用的工厂类
    handler

    等待队列满了之后,新提交任务的拒绝策略,线程池提供了几种常见的策略:

    AbortPolicy  -----------    抛出RejectedExecutionException,默认策略

    DiscardPolicy  --------     直接丢弃

    DiscardOldestPolicy ----  丢弃最先等待的任务,将新提交的任务放到等待队列中

    CallerRunsPolicy  -------  使用提交任务的线程执行该任务 

    建议创建线程池时使用ThreadPoolExecutor类来创建,这是因为Executors类提供的创建线程池的方法由于未指定等待队列的大小,会有OOM的风险

    线程池执行的一般规则

    1、提交新任务时,不管核心线程是否有空闲,创建新的线程执行任务

    2、核心线程均未空闲,提交新任务时,创建新的线程,直到到达线程池最大线程数。

    3、线程池中线程数达到最大线程,并且均未空闲,提交新任务时,放到等待队列中,直到有线程空闲

    4、等待队列满了后,执行拒绝策略

    5、非核心线程空闲超过keepAliveTime后,释放线程

    6、核心线程空闲超过keepAliveTime后,不释放,除非设置allowCoreThreadTimeout为true,但是一般不建议这么做

    7、拒绝策略默认为AbortPolicy,但是抛出的RejectedExecutionException为非受检异常,有时会忘记捕获,如果不关心任务执行结果,可以使用直接丢弃策略

    8、对于Cache线程池,提交任务时,先看有没有空闲线程,有直接使用;没有则创建线程;线程空闲60s,则从缓存中移除

    线程池任务的提交

    1、无返回值的提交

    2、有返回值的提交

     

     ExecutorService类其它方法介绍

    1、invokeAny()方法

     方法入参为Callable类型的集合,方法返回值为其中一个执行成功任务的返回值;

    有一个任务执行成功或者执行任务过程中抛出异常,会取消其余的任务的执行

    示例:

        public static void main(String[] args) throws ExecutionException, InterruptedException {
    
            ExecutorService service = Executors.newFixedThreadPool(3);
    
            Set<Callable<String>> callableSet = new HashSet<>();
            callableSet.add(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    System.out.println("Call Task1");
                    return "Task1";
                }
            });
            callableSet.add(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    System.out.println("Call Task2");
                    return "Task2";
                }
            });
    
            String result = service.invokeAny(callableSet);
            System.out.println(result);
    
            service.shutdown();
        }

    执行结果1:

    执行结果2:

    2、invokeAll()

     

     方法入参为Callable类型的集合,方法返回值为任务执行结果Future的集合;

     但是有些任务可能由于执行异常而结束,但是我们无法通过返回结果区分这一点

    示例:

        public static void main(String[] args) throws ExecutionException, InterruptedException {
    
            ExecutorService service = Executors.newFixedThreadPool(3);
    
            Set<Callable<String>> callableSet = new HashSet<>();
            callableSet.add(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    System.out.println("Call Task1");
                    return "Task1";
                }
            });
            callableSet.add(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    System.out.println("Call Task2");
                    return "Task2";
                }
            });
    
            List<Future<String>> futureList = service.invokeAll(callableSet);
            futureList.forEach(it -> {
                try {
                    String result = it.get();
                    System.out.println(result);
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            });
    
            service.shutdown();
        }

    执行结果:

    线程池关闭

    有个例子,如果在main方法中使用线程池提交了任务,如果未关闭线程池,那么应用程序会继续保持运行状态

     线程池关闭相关的方法如下:

    1、shutdown

     该方法不会立即关闭线程池,而是不再接收新的任务,等到线程池中任务执行完成后,线程池真正关闭

    2、shutdownNow

     该方法会立即尝试结束线程池中正在执行的任务,并跳过已经提交并未执行的任务;但是正在执行的任务是否能够结束不确定

     该方法的返回值为从未执行过的任务

    3、awaitTermination

     该方法如果在线程池收到shutdown请求后执行,会阻塞主线程,直到所有任务执行完毕或者任务执行超时亦或者任务执行异常;

     返回值:如果任务执行超时,返回false;否则返回true,下面是推荐的关闭线程池的写法

    service.shutdown();
    if (!service.awaitTermination(80, TimeUnit.MILLISECONDS)) {
         service.shutdownNow();
    }

    4、isShutdown

    返回线程池是否被关闭

    5、isTerminated

    返回是否所有任务在线程池关闭时全部执行完成

  • 相关阅读:
    RDD弹性分布式数据集的基本操作
    spark-shell的Scala的一些方法详解
    浅谈架构
    关于MapReduce二次排序的一点解答
    mysql 和 hive 和分布式zookeeper和HBASE分布式安装教程
    2018暑假总结
    暑假总结07
    2018暑假总结06
    2018暑假总结05
    2018暑假总结04
  • 原文地址:https://www.cnblogs.com/sniffs/p/11625137.html
Copyright © 2011-2022 走看看