ExecutorService —— 多线程分享(二)

ExecutorService多线程分享

一、概念梳理:

A:有一堆任务:
B:执行这堆任务:
C:获取这堆任务信息,以及影响任务的执行:
A: 任务

Runnable(接口) :run(),执行任务,无返回值
Callable (接口):call(),执行任务,有返回值
FutureTask(类):根据不同的构造器,构建一个task实例
在这里插入图片描述在这里插入图片描述
(构造器的作用是传入一个实现了Runnable接口的任务类,并设置当此任务类运行完成后,任务类需要向调用此类的get方法的对象返回的 结果(result),此构造器会调用Executors类的静态方法 callable(Runnable task, T result) )
FutureTask同时间接实现了Runnable和Future,(FutureTask实现了RunnableFuture,RunnableFuture继承了Runnable, Future)也就是说它既可以做为线程被执行,又可以作为Future得到Callavble的返回值。

B: 任务执行器

Executor(接口):只能执行,无返回值
ExecutorService(接口):继承了Executor,丰富了调用方式,以及控制(shutdown,invokeAll,submit…)
ThreadPoolExecutor:实现了Executor和ExecutorService,是真正实现功能干活的。 通过Executors工厂类去构件返回ThreadPoolExecutor实例。

C:

Future(接口): 通过提供的5个方法,实现了三个功能
1.判断任务是否完成
2.能够中断任务
3.能够获取任务的执行结果
A: isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消 成 功,则返回 true。
B: isDone方法表示任务是否已经完成,若任务完成,则返回true;

C: get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
D: get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

E:boolean cancel(boolean mayInterruptIfRunning);

方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。

因为它是一个接口,实际干活的是FutureTask,且FutureTask是Future的唯一实现类

二、ExecutorService

ExecutorService 是JDK提供的,通过Executors类的工厂方法可以创建不同策略的线程池返回的实际是ThreadPoolExecutor:
ExecutorService —— 多线程分享(二)_第1张图片
2.1 ExecutorService pool = Executors.newCachedThreadPool(); ---- 缓存

可以有无限大的线程数进来(线程地址不一样),但需要注意机器的性能,需要线程太多,会导致服务器出现问题。

2.2 ExecutorService pool = Executors.newFixedThreadPool(2); -----固定
在线程池中保持二个线程可以同时执行,并不是说线程池中永远都是这二个线程,只是说可以同时存在的线程数,当某个线程执行结束后,会有新的线程进来

2.3.ScheduledExecutorService n pool = Executors.newScheduledThreadPool(); ----- 计划定时
public ScheduledFuture scheduleAtFixedRate(Runnable command, ---- 任务线程
long initialDelay, ---- 首次执行
long period, ---- 首次间隔
TimeUnit unit); ----时间粒度

2.4.ExecutorService pool = Executors.newSingleThreadExecutor(); ---- 单个
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务。

三、 Executors.newFixedThreadPool();

Executors : 功能就是线程池对线程的一个管理功能

3.1 List invokeAll(Collection tasks)

批量提交不限时任务 :

3.2 List invokeAll(Collection tasks, long timeout, TimeUnit unit)

批量提交限时任务 :
其中tasks是任务集合,timeout是超时时间,unit是时间单位
这里的超时时间是针对的所有tasks,而不是单个task的超时时间。如果超时,会取消没有执行完的所有任务,并抛出超时异常。

3.3 shutdown():停止接收新任务,原来的任务继续执行

3.4 shutdownNow(); 直接关闭,不管还有没有任务执行,并返回尚未执行的task的list

3.5 isShutdown(): 而只要执行过shutdown()方法,isShutdown()就为true;

3.6 isTerminated(); 而当其中任务执行完成的时候,isTerminated()才为true;

3.7 awaitTermination(long timeout, TimeUnit unit) :
接收timeout和TimeUnit两个参数,用于设定超时时间及单位。当等待超过设定时间时,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用。

ExecutorService将不会自动销毁。它会一直保持存活并且等待执行新任务。

四、阿里巴巴为什么建议手动创建线程池

1.不建议使用的原因:
在这里插入图片描述
阿里规约之所以强制要求手动创建线程池,也是和这些参数有关。具体为什么不允许,规约是这么说的:

线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同事更加明确线程池的运行规则,规避资源耗尽的风险。
ExecutorService —— 多线程分享(二)_第2张图片
1.corePoolSize 指定了线程池里的线程数量,核心线程池大小
2.maximumPoolSize 指定了线程池里的最大线程数量
3.keepAliveTime 当线程池线程数量大于corePoolSize时候,多出来的空闲线程,多长时间会被销毁。
4.unit 时间单位
5.workQueue 任务队列,用于存放提交但是尚未被执行的任务。
6.threadFactory 线程工厂,用于创建线程,一般可以用默认的
7.handler 拒绝策略,当任务过多时候,如何拒绝任务。当提交任务数超过maximumPoolSize + workQueue 的size之和,任务交给RejectedExecutionHandler 处理

说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
  主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
  主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
  ExecutorService —— 多线程分享(二)_第3张图片
  看一下这两种弊端怎么导致的。

第一种是因为,创建了一个size为Integer.MAX_VALUE的线程阻塞队列,可能会堆积大量的请求,消耗很大的内存,甚至导致OOM。
在这里插入图片描述
第二种是因为,创建了的线程池允许的最大线程数是Integer.MAX_VALUE,可能会创建大量的线程,消耗资源,甚至导致OOM。
在这里插入图片描述

2.创建线程池的正确姿势:
ExecutorService —— 多线程分享(二)_第4张图片

2.1:内部处理逻辑:
2.1.1:如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

2.1.2:如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。

2.1.3:如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
ExecutorService —— 多线程分享(二)_第5张图片

2.2四种策略:
2.2.1:ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
2.2.2:ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
2.2.3:ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
2.2.3:ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

4 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。

抛砖引玉,如有错误之处请大佬们指正~

你可能感兴趣的:(java)