线程池

一、前期知识概要

1、设计模式对象池(资源池)

在我们的日常生活我们听过水池,电池等等,水池了用来存放水,电池用来存放电,而在编程的世界中的池是用来存放一组资源

资源池(resource pool)被认为是一种设计模式,这里的资源主要是指系统资源, 这些资源不专属于某个进程或内部资源。客户端向池请求资源, 并使用返回的资源进行指定的操作。当客户端使用完资源后, 会把资源放回池中而不是释放或丢弃掉。

总结一句话: 需要时,从池中提取,不用时,放回池中

举个栗子: 对象池就想我们公司的仓库,比如我们去公司上班,公司会给我们提供一个工位,行政人员会给我们提供相应的办公设备,那这个时候她首先会看一下库房中,如果库房中有,直接从库房中拿,如果库房中没有,那就会去网上或者商店购买一个新的。如果员工离职了正常情况下会将员工的能用的办公物品放到库房

image

设计模式

2、应用场景

它用在当对象的初始化过程代价较大或者使用频率较高时,比如线程池,数据库连接池等。运用对象池化技术可以显著地提升性能。

二、为什么要使用

创建线程对象不像其他对象一样在JVM分配内存即可,还要调用操作系统内核的API,然后操作系统为线程分配一系列的资源,这个成本就很高了。所以线程是一个重量级对象,应该避免频繁创建和销毁

降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

提高响应速度。 当任务到达时,任务可以不需要的等到线程创建就能立即执行。

提高线程的可管理性。 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

引用《Java并发编程的艺术》

三、Java线程池的架构设计

1、说明

Java里面线程池的顶级接口是Executor,该类位于java.util.concurrent,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

2、重要类说明

类或者接口 说明
ExecutorService 真正的线程池接口。
ScheduledExecutorService 定时任务与线程池功能结合使用
ThreadPoolExecutor ExecutorService的默认实现。重点
ScheduledThreadPoolExecutor 周期性任务调度。

3、结构图

image
image

4、Executor

  1. 说明

    Executor接口只有一个execute方法,执行提交Runnable任务,用来替代通常启动线程的方法

  2. 方法

    execute(Runnable r)
    
  3. 举个栗子

    /*以前*/
    Thread t = new Thread();
    t.start();
    
    /*使用线程池*/
    Thread t = new Thread();
    executor.execute(t)
    

5、ExecutorService

  1. 说明

    ExecutorService接口继承自Executor接口,真正的线程池核心类。提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。增加了shutDown(),shutDownNow(),invokeAll(),invokeAny()和submit()等方法。如果需要支持即时关闭,也就是shutDownNow()方法,则任务需要正确处理中断。

  2. 核心方法

    方法名 返回值 说明
    submit(Callable task) Future 提交一个可运行的任务执行,并返回一个表示该任务结果
    submit(Runable task) Future 提交一个可运行的任务执行,并返回一个表示该任务结果
    shutdown() 布尔 阻止新来的任务提交,对已经提交了的任务不会产生任何影响。当已经提交的任务执行完后,它会将那些闲置的线程进行中断,这个过程是异步的
    shutdownNow() List 设置线程池的状态为STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
    isShutdown() 布尔 检测线程池是否正处于关闭中
    isTerminated() 布尔 所有任务在关闭后完成,则返回 true
    awaitTermination() 布尔 定时或者永久等待线程池关闭结束
  3. 举个栗子

        private static int TASK_COUNT = 10;
        public static void main(String[] args) {
            /*1. 创建线程池对象 */
            ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 8, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy()););
            /*2. 提交任务*/
            for (int i = 0; i < TASK_COUNT; i++) {
                pool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + ":----->在执行任务");
                });
            }
            /*3. 关闭连接池*/
            pool.shutdown();
           /* 或者 */
         // pool.shutdownNow()
        }
    

6、ScheduledExecutorService

  1. 说明

    ScheduledExecutorService是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,任务是并发执行,互不影响。

  2. 核心方法

  1. 关系图

    image
  2. 示例代码

        public static void main(String[] args) {
            // 创建定时任务线程池
            ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
            //设置日期格式
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                 // 提交一个任务两秒之后开始执行
            executorService.schedule(() -> {
                System.out.println("++++++++++++++++++++子线程:" + df.format(new Date()));
            }, 2, TimeUnit.SECONDS);
            System.out.println("主线程:    " + df.format(new Date()));
    //        executorService.shutdown();
        }
    

7、工作流程(了解)

image

四、线程池的状态与生命周期

1、概要

线程池有运行、关闭、停止、清空状态、结束五种状态,结束后就会释放所有资源

  1. RUNNING(运行): 接受新的任务和处理队列中的任务
  2. SHUTDOWN(关闭): 不接受新的请求,但会处理已经添加到队列中的任务
  3. STOP(停止): 不接收新任务,也不处理队列任务,并且中断所有处理中的任务。
  4. TIDYING(整理):所有任务都被终结,有效线程为0。会触发terminated()方法
  5. TERMINATED(结束):当terminated()方法执行结束

2、流程图

image

五、ThreadPoolExecutor

1、线程池的创建

  1. 构造方法

    ThreadPoolExecutor(int corePoolSize,
                       int maximumPoolSize,
                       long keepAliveTime,
                       TimeUnit unit,
                       BlockingQueue workQueue,
                       ThreadFactory threadFactory,
                       RejectedExecutionHandler handler)
    

2、参数概要

参数 概要
corePoolSize 池中所保存的线程数,包括空闲线程。
maximumPoolSize 池中允许的最大线程数。
keepAliveTime 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit - keepAliveTime 时间单位。
workQueue 执行前用于保持任务的队列。此队列仅保持由 execute方法提交的 Runnable任务。
threadFactory 执行程序创建新线程时使用的工厂
handler 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。

3、参数详解

3.1、corePoolSize(必要参数)

核心线程数。默认情况下,核心线程会一直存活,但是当将allowCoreThreadTimeout设置为true时,核心线程也会超时回收。

3.2、maximumPoolSize(必要参数)

线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。

3.3、keepAliveTime(必要参数)

线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。

3.4、unit(必要参数)

指定keepAliveTime参数的时间单位

可选值 说明
TimeUnit.DAYS
TimeUnit.HOURS 小时
TimeUnit.MINUTES(常用) 分钟
TimeUnit.SECONDS(常用)
TimeUnit.MILLISECONDS(常用) 毫秒
TimeUnit.MICROSECONDS 微秒(千分之一毫秒)
TimeUnit.NANOSECONDS 毫微秒(千分之一微秒)

3.5、workQueue

任务队列。Runnable对象就存储在该参数中

3.6、threadFactory(可选)

线程工厂。用于指定为线程池创建新线程的方式

3.7、handler(可选)

  1. 说明

    拒绝策略。有两种情况会触发拒绝策略

    • 队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是 AbortPolicy,表示无法处理新任务时抛出异常
    • 当线程池被调用shutdown()后
  2. 可选值

    策略 说明
    AbortPolicy 直接抛出异常。默认值
    CallerRunsPolicy 只用调用者所在线程来运行任务。
    DiscardOldestPolicy 丢弃队列里最近的一个任务,并执行当前任务。
    DiscardPolicy 不处理,丢弃掉。

4、举个栗子

  1. 有返回值

     public static void start() {
            /*
             * 创建线程池,并发量最大为5
             * LinkedBlockingDeque,表示执行任务或者放入队列
             */
            ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 0,
                    TimeUnit.SECONDS, new LinkedBlockingDeque<>());
            // 存储线程的返回值
            List> results = new LinkedList<>();
            for (int i = 0; i < 10; i++) {
                // 调用submit可以获得线程的返回值
                int num = i;
                Future result = executor.submit(() -> num + "");
                results.add(result);
            }
            //如果不调用,awaitTermination将一直阻塞
            executor.shutdown();
            //1天,模拟永远等待
            try {
                System.out.println(executor.awaitTermination(1, TimeUnit.DAYS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            //输出结果
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(results.get(i).get());
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }
    

5、corePoolSize、workQueue 、maximumPoolSize的关系

  1. 默认情况下,线程池在初始的时候,线程数为0。当接收到一个任务时,如果线程池中存活的线程数小于corePoolSize核心线程,则新建一个线程。
  2. 如果所有运行的核心线程都都在忙,超出核心线程处理的任务,执行器更多地选择把任务放进队列,而不是新建一个线程。
  3. 如果一个任务提交不了到队列,在不超出最大线程数量情况下,会新建线程。就根据指定的拒绝策略来处理,默认抛出异常。
  4. 如线程闲置时,线程池会根据keepAliveTime设置的时间回收大于corePoolSize的线程

六、ScheduledThreadPoolExecutor

1、简介

ScheduledThreadPoolExecutor用来执行周期性任务的调度。在这之前的实现需要依靠Timer和TimerTask或者其它第三方工具来完成。它主要有以下两个作用

  1. 延时执行任务。
  2. 周期性重复执行任务。
Timer ScheduledThreadPoolExecutor
单线程 多线程
单个任务执行时间影响其他任务调度 多线程,不会影响
基于绝对时间 基于相对时间
一旦执行任务出现异常不会捕获,其他任务得不到执行 多线程,单个任务的执行不会影响其他线程

2、示例代码

  1. 执行一次
    private static final int TASK_COUNT = 3;

    public static void main(String[] args) throws InterruptedException {
        // 创建大小为2的线程池
        ScheduledExecutorService scheduledThreadPool = new ScheduledThreadPoolExecutor(2);
        for (int i = 0; i < TASK_COUNT; i++) {
            // 只执行一次
          scheduledThreadPool.schedule(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, 5, TimeUnit.SECONDS);

        }

        // 关闭线程池
        scheduledThreadPool.shutdown();
        boolean isStop;
        // 等待线程池终止
        do {
            isDone = scheduledThreadPool.awaitTermination(1, TimeUnit.HOURS);
            System.out.println("等待任务结束中...");
        } while (!isStop);
        System.out.println("所有工作完成!!! 线程池关闭");
    }

  1. 周期执行任务

        private static final int TASK_COUNT = 3;
    
        public static void main(String[] args) throws InterruptedException {
            // 1. 创建大小为2的线程池
            ScheduledExecutorService scheduledThreadPool = new ScheduledThreadPoolExecutor(2);
                // 2. 周期性执行,每2秒执行一次
                scheduledThreadPool.scheduleAtFixedRate(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                        System.out.println(Thread.currentThread().getName());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }, 0, 2, TimeUnit.SECONDS);
            }
    
            // 3.关闭线程池
            scheduledThreadPool.shutdown();
            boolean isStop;
            // 等待线程池终止
            do {
                isStop = scheduledThreadPool.awaitTermination(1, TimeUnit.HOURS);
                System.out.println("等待任务结束中...");
            } while (!isStop);
             System.out.println("所有工作完成!!! 线程池关闭");
        }
    

七、任务队列

1、名词解释

1.1、什么叫有界

有界就是有固定大小的队列,无界表示无上限

1.2、什么叫队列

Queue 一个队列就是一个先入先出(FIFO)的数据结构

Queue接口与List、Set同一级别,都是继承了Collection接口。

2、常用队列

2.1、ArrayBlockingQueue

  1. 作用

    采用数组实现的有界阻塞线程安全队列。如果向已满的队列继续塞入元素,将导致当前的线程阻塞。如果向空队列获取元素,那么将导致当前线程阻塞。

  2. 构造方法

    构造方法 参数说明
    public ArrayBlockingQueue(int capacity) 构造指定大小的有界队列
    public ArrayBlockingQueue(int capacity, boolean fair) 构造指定大小的有界队列,指定为公平或非公平锁
    public ArrayBlockingQueue(int capacity, boolean fair, Collection c) 构造指定大小的有界队列,指定为公平或非公平锁,指定在初始化时加入一个集合
  3. 示例代码

    public class ArrayBlockingQueueExample {
        public static final int COUNT = 100;
    
        public static void main(String[] args) throws Exception {
            ArrayBlockingQueue queue = new ArrayBlockingQueue<>(10);
            ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 8, 60, TimeUnit.SECONDS, queue);
            for (int i = 0; i < COUNT; i++) {
                TimeUnit.SECONDS.sleep(1);
                executor.execute(() ->
                        System.out.println("线程池---数组实现的有界阻塞线程安全队列" + Thread.currentThread().getName()));
            }
            executor.shutdown();
        }
    }
    

2.2、LinkedBlockingQueue

  1. 作用

    一个由链表结构组成的有界阻塞队列(也可以当无界阻塞队列)。此队列按 FIFO(先进先出)原则。Executor.newFixedThreadPool()默认队列

  2. 构造方法

    构造方法 参数说明
    public LinkedBlockingQueue() 在未指明容量时,容量默认为Integer.MAX_VALUE
    public LinkedBlockingQueue(int capacity) 构造指定大小的有界队列

2.3、SynchronousQueue;

  1. 作用

    一个不存储元素的有界阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool()使用了这个队列。

  2. 构造方法

    构造方法 参数说明
    public SynchronousQueue() 默认情况下不保证排序,
    public SynchronousQueue(boolean fair) 如果设置true队列可保证线程以 FIFO 的顺序进行访问
  3. 示例代码

    public class SynchronousQueueExample {
        public static final int COUNT = 100;
    
        public static void main(String[] args) {
            SynchronousQueue queue = new SynchronousQueue<>();
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, Integer.MAX_VALUE, 1, TimeUnit.SECONDS, queue);
            for (int i = 0; i < COUNT; i++) {
                executor.execute(() ->
                        System.out.println("线程池---同步队列" + Thread.currentThread().getName()));
            }
            executor.shutdown();
        }
    }
    
  4. 分析

    • 假设当前有2个核心线程

    • 此时来了一个任务(A),根据前面介绍的“如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。”,所以A被添加到queue中。

    • 又来了一个任务(B),且核心2个线程还没有忙完。接下来首先尝试1中描述,但是由于使用的SynchronousQueue,所以一定无法加入进去。

    • 此时便满足了上面提到的“如果无法将请求加入队列,则创建新的线程”,所以必然会新建一个线程来运行这个任务。

    • 但是如果这三个任务都还没完成,继续来了一个任务,queue中无法插入(任务A还在queue中),而线程数达到了maximumPoolSize,所以只好执行异常策略了。
      为了避免这种情况:,所以在使用SynchronousQueue通常要求maximumPoolSize是无界的(如果希望限制就直接使用有界队列)。对于使用SynchronousQueue的作用jdk中写的很清楚:此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。

2.4、PriorityBlockingQueue

  1. 作用

    一种优先级队列,元素并不是以FIFO的方式出/入队。默认大小为11,不可以插入 null 值。当队列满的时候会进行扩容,是真正意义上的无界(仅受内存大小限制),它不像ArrayBlockingQueue那样构造时必须指定最大容量,也不像LinkedBlockingQueue默认最大容量为Integer.MAX_VALUE;

  2. 构造方法

    构造方法 参数说明
    PriorityBlockingQueue()
    PriorityBlockingQueue(int initialCapacity) 指定初始化队列长度
    PriorityBlockingQueue(int initialCapacity, Comparator comparator) 指定初始化队列长度,自定义比较器
  3. 示例代码

    
    
    
    
    

八、Executors(了解)

1、说明

对于新手来说要配置一个线程池还是比较有难度的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池

2、注意注意注意

image

3、Executors静态方法

1、newSingleThreadExecutor

  1. 作用

    创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。

  2. 方法

    Executors.newSingleThreadExecutor()
    
  3. 应用场景

    保证所有任务的执行顺序按照任务的提交顺序执行

    不适合并发但可能引起IO阻塞性及影响UI线程响应的操作,如数据库操作、文件操作等

2、newFixedThreadExecutor

  1. 作用

    创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程

  2. 方法

    Executors.newFixedThreadExecutor()
    
  3. 应用场景

    控制线程最大并发数

  4. 举个栗子

    
    

3、newCacheThreadExecutor

  1. 作用

    创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,

    那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小

  2. 方法

    Executors.newCacheThreadExecutor()
    
  3. 应用场景

    适合执行大量、耗时少的任务

  4. 举个栗子

    
    

4、newScheduleThreadExecutor

  1. 作用

    创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求

  2. 方法

    Executors.newScheduleThreadExecutor()
    
  3. 示例代码

    // 1. 创建 定时线程池对象 & 设置线程池线程数量固定为5
    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
    // 2. 创建好Runnable类线程对象 & 需执行的任务
    Runnable task =new Runnable(){
     public void run() {
     System.out.println("执行任务啦");
     }
    };
    // 3. 向线程池提交任务
    scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延迟1s后执行任务
    scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延迟10ms后、每隔1000ms执行任务
    

九、面试题

1、shutdown

  1. shutdown()有什么作用?

阻止新来的任务提交,对已经提交的任务不会产生任何影响 当已经提交的任务执行完成之后,那些闲置的线程会被回收
这个过程是异步的。

  1. 如何阻止新来的任务提交?

通过将线程池的状态改成SHUTDOWN,当再将执行execute提交任务时,如果测试到状态不为RUNNING,则抛出rejectedExecution,从而达到阻止新任务提交的目的。

  1. 为何对提交的任务不产生任何影响?

在调用中断任务的方法时,它会检测workers中的任务,如果worker对应的任务没有中断,并且是空闲线程,它才会去中断。另外的话,workQueue中的值,还是按照一定的逻辑顺序不断的往works中进行输送的,这样一来,就可以保证提交的任务按照线程本身的逻辑执行,不受到影响。

2、shutdownNow

  1. shutdownNow()有什么功能?

    阻止新来的任务提交,同时会中断当前正在运行的线程,即workers中的线程。另外它还将workQueue中的任务给移除,并将这些任务添加到列表中进行返回。

  2. 如何阻止新来的任务提交?

    通过将线程池的状态改成STOP,当再将执行execute提交任务时,如果测试到状态不为RUNNING,则抛出rejectedExecution,从而达到阻止新任务提交的目的.

  3. 如果我提交的任务代码块中,正在等待某个资源,而这个资源没到,但此时执行shutdownNow(),会出现什么情况?

    当执行shutdownNow()方法时,如遇已经激活的任务,并且处于阻塞状态时,shutdownNow()会执行1次中断阻塞的操作,此时对应的线程报InterruptedException,如果后续还要等待某个资源,则按正常逻辑等待某个资源的到达。例如,一个线程正在sleep状态中,此时执行shutdownNow(),它向该线程发起interrupt()请求,而sleep()方法遇到有interrupt()请求时,会抛出InterruptedException(),并继续往下执行。在这里要提醒注意的是,在激活的任务中,如果有多个sleep(),该方法只会中断第一个sleep(),而后面的仍然按照正常的执行逻辑进行。

附: 线程池工具类

public class ThreadPoolUtil {
    /**
     * 核心线程池大小
     */
    private static final int CORE_POOL_SIZE = 20;

    /**
     * 最大可创建的线程数
     */
    private static final int MAX_POOL_SIZE = 100;

    /**
     * 线程池维护线程所允许的空闲时间
     */
    private static final int KEEP_ALIVE_SECONDS = 5;

    /**
     * 队列最大长度
     */
    private static final int QUEUE_CAPACITY = 1000;

    public volatile static ExecutorService pool;

    /**
     * 获取线程池
     * @return
     */
    public static ExecutorService getPool(){
        if(null == pool){
            synchronized (ThreadPoolUtil.class) {
                if (null == pool) {
                    pool = new ThreadPoolExecutor(
                            CORE_POOL_SIZE,
                            MAX_POOL_SIZE,
                            KEEP_ALIVE_SECONDS,
                            TimeUnit.SECONDS,
                            new LinkedBlockingDeque<>(QUEUE_CAPACITY),
                            Executors.defaultThreadFactory(),
                            new ThreadPoolExecutor.CallerRunsPolicy());
                }
            }
        }
        return pool;
    }

    /**
     * 获取线程池
     * @return
     */
    public static ExecutorService getPoolNew(){
        if(null == pool){
            synchronized (ThreadPoolUtil.class) {
                if (null == pool) {
                    /**
                     * 1. 缓存线程池
                     * {@Code Executors.newCachedThreadPool()}
                     * corePoolSize=0, maxinumPoolSize=+∞,队列长度=0 ,
                     * 因此线程数量会在corePoolSize到maxinumPoolSize之间一直灵活缓存和变动,
                     * 且不存在队列等待的情况,一来任务我就创建,用完了会释放。
                     *
                     * 2. 定长线程池
                     * {@Code Executors.newFixedThreadPool()}
                     * corePoolSize= maxinumPoolSize=构造参数值, 队列长度=+∞。因此不存在线程不够时扩充的情况
                     *
                     * 3. 定时器线程池
                     * {@Code Executors.newScheduledThreadPool()}
                     * 提交定时任务用的,构造参数里会带定时器的间隔和单位。 其他和FixedThreadPool相同,属于定长线程池。
                     *
                     * 4. 单线程池
                     * {@Code Executors.newScheduledThreadPool()}
                     * corePoolSize=maxinumPoolSize=1, 队列长度=+∞,只会跑一个任务, 所以其他的任务都会在队列中等待,因此会严格按照FIFO执行
                     */
                    pool = Executors.newFixedThreadPool(50);
                }
            }
        }
        return pool;
    }

    /**
     * 执行线程(无返回值)
     */
    public static void execute(ThreadExecuteTask threadExecuteTask,Object... objects){
        ExecutorService pool = getPool();
        pool.execute(() -> {
            threadExecuteTask.execute(objects);
        });
    }

    /**
     * 执行线程(有返回值)
     */
    public static  Future submit(ThreadSubmitTask threadSubmitTask,Object... objects){
        ExecutorService pool = getPool();
        Future future = pool.submit(() -> {
            return threadSubmitTask.submit(objects);
        });
        return future;
    }
}

interface ThreadExecuteTask{
    /**
     * 无返回值方法
     * @param objects
     */
    void execute(Object... objects);
}

interface ThreadSubmitTask{
    /**
     * 有返回值方法
     * @param objects
     * @return
     */
    T submit(Object... objects);
}

你可能感兴趣的:(线程池)