ExecutorService - 执行器服务

一、概述

1.线程池的意义:减少线程的创建和销毁,做到线程的重用,以提高资源的利用率

2.当线程池定义好之后,线程池中没有任何线程

3.每过来一个请求,会创建一个线程去处理这个请求,直到线程数达到指定的数量,

   就不再创建,这些线程称之为核心线程

4.在核心线程数量达到指定的线程数量之前,每次的请求都会去创建一个新的核心线程

5.核心线程使用完成后,不会被销毁,而是等待下一个任务

6.如果核心线程被全部占满,后续的请求会放入线程池的工作队列中,工作队列本质上是阻塞式队列

7.如果工作队列被全部占用,则新来的请求会交给临时线程来处理

8.临时线程的数量也是有限的,临时线程用完之后不会立即销毁而是存活一段指定的时间,

   如果这段时间内没有请求处理,则该临时线程才会被销毁

9.如果临时线程也全都被占满了,则后续的请求会被交给拒绝执行处理器(RejectedExecutionHandler)来进行拒绝处理

ThreadPoolExecutor:

package com.apple.pool;

import java.util.concurrent.*;

/**
 * This is Description
 *
 * @author apple
 * @date 2020/06/08
 */
public class ExecutorServiceDemo {
    public static void main(String[] args) {

        //创建线程池
        //corePoolSize - 核心线程数量
        // maximumPoolSize - 最大线程数 = 核心线程数 + 临时线程数
        // keepAliveTime - 临时线程存活时间
        // unit - 时间单位
        // workQueue - 工作队列(本质上是阻塞队列)
        // handler - 拒绝执行处理器
        ExecutorService es = new ThreadPoolExecutor(
                5, // 5个核心
                10, // 5个核心 + 5个临时
                5, // 存活时间
                TimeUnit.SECONDS,// 时间单位
                new ArrayBlockingQueue<>(5),// 工作队列
                new RejectedExecutionHandler() {// 拒绝执行处理器
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        System.out.println(r + "线程被拒绝");
                    }
                });

        //int n = 12; // 5个给核心,5个给工作队列,还剩下2个给临时线程,故一开始是7个线程执行,剩下2个再执行
        int n = 18; // 5个给核心,5个给工作队列,还剩下8个中,5个给临时线程,还剩下3个被拒绝,故一开始10个线程执行,10个正常,3个被拒绝,剩下5个再执行
        //执行线程
        for (int i = 0; i < n; i++) {
            es.execute(new EsRunnable());
        }

        //关闭线程池
        es.shutdown();

        //new Thread(new EsRunnable()).start();
    }
}

class EsRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("hello ~~~");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程工具类:

package com.apple.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * This is Description
 *
 * @author apple
 * @date 2020/06/08
 */
public class ExecutorServiceDemo2 {
    public static void main(String[] args) {

        /**
         * 特点:
         * 1.没有核心线程,全都是临时线程
         * 2.临时线程的数量为 Integer.MAX_VALUE,人为的认为这个线程池可以承载的线程数量是无限
         * 3.临时线程的存活时间是 60L SECONDS - 1min
         * 4.工作队列是一个同步队列(线程安全) - 同步队列的容量位1 - 只能存储1个元素
         * 大池子小队列
         * 使用场景:
         * 只用于高并发短任务的场景,例如:即时通讯(发送微信,QQ聊天等)
         * 不适用:例如百万人同时下载一个视频,或者多人在线同时观看一个视频
         */
        ExecutorService es = Executors.newCachedThreadPool();

        /**
         * 特点:
         *1.没有临时线程,全都是核心线程,因为 核心线程和临时线程数量一样,且临时线程存活的时间为 0L
         *2.工作队列是一个链式队列,默认容量是 Integer.MAX_VALUE
         *  人为的认为这个队列是无限的,可以缓存足够多的任务
         *  大队列小池子
         * 使用场景:
         * 不适用于高并发短任务场景,而适用于长任务的场景,例如:下载视频和文件,百度云盘
         */
        ExecutorService es2 = Executors.newFixedThreadPool(5);
    }
}

 

二、Callable线程

1.Callable 执行完成之后会有返回结果,所以泛型限定结果类型

2.Runnable和Callable的区别:

  a.返回值:Runnable没有返回值,Callable有返回值

  b.启动方式:Runnable可以通过Thread启动,也可以通过线程池启动

         Callable只能通过线程池启动

  c.容错处理:Runnable的实现run()方法不能异常抛出,所以不能以全局方式(例如Spring中的异常通知)处理;

         Callable允许以全局方式处理

package com.apple.pool;

import java.util.concurrent.*;

/**
 * This is Description
 *
 * @author apple
 * @date 2020/06/08
 */
public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        ExecutorService es = Executors.newCachedThreadPool();

        //既可以执行Runnable 也可以执行Callable
        //将返回值封装成一个Future对象
        Future<String> f = es.submit(new CDDemo());
        System.out.println(f.get());
    }
}

//返回值类型是String
class CDDemo implements Callable<String> {

    @Override
    public String call() throws Exception {
        return "SUCCESS";
    }
}

 

三、ScheduleExecutorService - 定时执行器服务

1.起到定时执行的效果

2.是很多定时器的底层实现

package com.apple.pool;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * This is Description
 *
 * @author apple
 * @date 2020/06/08
 */
public class ScheduleExecutorServiceDemo {
    public static void main(String[] args) {

        /**
         * 创建线程池
         */
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(5);

        /**
         * Callable 或 Runnable - 要执行的线程
         * delay - 延时时间
         * unit - 时间单位
         * 推迟指定的时间再执行提交的任务
         */
        // ses.schedule(new ScheduleRunnable(), 5, TimeUnit.SECONDS);

        /**
         * Runnable - 要执行的线程
         * initialDelay - 延时时间
         * period - 间隔时间
         * unit - 时间单位
         * 从上一次的起始时间计时,推算下一次的起始时间
         * 如果线程额执行时间大于间隔时间,则以时间执行时间为准
         */
        // ses.scheduleAtFixedRate(new ScheduleRunnable(), 0, 5, TimeUnit.SECONDS);

        /**
         * Runnable - 要执行的线程
         * initialDelay - 延时时间
         * period - 间隔时间
         * unit - 时间单位
         * 从上一次的结束时间,推算下一次的起始时间
         */
        ses.scheduleWithFixedDelay(new ScheduleRunnable(), 0, 5, TimeUnit.SECONDS);
    }
}

class ScheduleRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("hello ~~~");
        try {
            //如果为3000,则 scheduleAtFixedRate()方法还是每隔5秒执行,scheduleWithFixedDelay()方法会在 间隔时间+3秒
            //Thread.sleep(3000);
            //如果为8000,则 scheduleAtFixedRate()方法还是每隔8秒执行,scheduleWithFixedDelay()方法会在在 间隔时间+秒
            Thread.sleep(8000);        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

四、ForkJoinPool - 分叉合并池

1.分叉:将任务进行分解,将一个大任务拆分成多个小的任务分配到不同的核上来执行

2.合并:将分叉的任务的执行结果进行汇总

3.分叉合并的目的:提高CPU的利用率

4.分叉合并是适用于任务量比较大的场景

5.为了防止因为慢任务导致效率降低,采用work-srealing(工作窃取)策略:即

 当一个核上的任务执行完成之后,不会空闲下来而是会随机的去其他核上去“偷”一个任务去执行

package com.apple.pool;

import java.util.concurrent.*;

/**
 * This is Description
 *
 * @author apple
 * @date 2020/06/08
 */
public class ForkAndJoinPoolDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        ForkJoinPool pool = new ForkJoinPool();
        final ForkJoinTask<Long> f = pool.submit(new Sum(1, 100000000000L));
        System.out.println(f.get());
    }
}

//RecursiveTask - 有返回值
//RecursiveAction - 没有返回值
class Sum extends RecursiveTask<Long> {

    private long start;
    private long end;

    public Sum(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {

        //每隔10000个数进行求和
        //大于10000个数进行求和
        if (end - start > 10000) {
            long mid = (start + end) / 2;
            Sum left = new Sum(start, mid);
            Sum right = new Sum(start + 1, end);
            //分叉
            left.fork();
            right.fork();
            //合并
            return left.join() + right.join();
        } else { //如果小于10000个数,就进行求和
            long sum = 0;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        }

    }
}

 

posted @ 2020-06-08 20:52  alen-fly  阅读(236)  评论(0编辑  收藏  举报