并发编程0014 --- 线程池的使用

什么是线程池

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

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

 

posted @ 2019-10-05 17:06  光头用沙宣  阅读(167)  评论(0编辑  收藏  举报