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

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

  • 相关阅读:
    python基础练习题(题目 画菱形)
    python基础练习题(题目 两个乒乓球队进行比赛,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单)
    python基础练习题(题目 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少)
    python基础练习题(题目 一个数如果恰好等于它的因子之和,这个数就称为"完数"。例如6=1+2+3.编程找出1000以内的所有完数)
    python基础练习题(题目 求s=a+aa+aaa+aaaa+aa…a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加由键盘控制)
    酒库重构测试—半自动化验证
    python基础练习题(题目 输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数)
    python基础练习题(题目 将一个整数分解质因数。例如:输入90,打印出90=2*3*3*5)
    python基础练习题(题目 打印出所有的"水仙花数",所谓"水仙花数"是指一个三位数,其各位数字立方和等于该数本身)
    python基础练习题(题目 判断101-200之间有多少个素数,并输出所有素数。)
  • 原文地址:https://www.cnblogs.com/sniffs/p/11625137.html
Copyright © 2011-2022 走看看