JUC-executor
一:概念
1:Executor(执行者)
Executor是Java里面线程池的顶级接口;
2:ExectorService
继承自Executor的接口,可以看作是Excutor接口功能的扩充,这个接口提供了下面方法;在任务发布以后可以得到一个future对象,从而实现对任务结果的异步获得.
<T> Future<T> submit(Callable<T> var1);
-
注:execute()方法只能接收Runnable 对象。submit()方法可以接收Runnable和 Callable类型的对象。
-
submit() 方法可以返回持有计算结果的 Future 对象,可以判断任务是否执行成功,而 execute() 方法不可以。
3:ThreadExectorPool
继承了ExecutorService接口,可以看作是以上两个接口的实现类
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
二:ThreadExectorPool七个参数
1:corePoolSize
核心线程数,这些线程不会被释放
2:maximumPoolSize
最大线程数,最大可以运行的线程数量
3:keepAliveTime
线程未使用,存活时间,超出核心线程的数量的那些线程,在存活时间内未使用,则释放线程。
4:TimeUnit
keepAliveTime的时间单位,比如分钟,小时等
5:BlockingQueue
缓冲队列,
6:ThreadFactory
线程工厂用来创建新的线程放入线程池
7:RejectedExecutionHandler
线程池拒绝任务的处理策略,比如抛出异常等策略
三:ThreadExectorPool执行流程
1:一个新的线程启动;定义线程池的数量为curSize
2:curSize < corePoolSize,则线程工厂创建核心线程执行;
3:maximumPoolSize > curSizecurSize >= corePoolSize大小,并且缓冲队列未满;则线程进入缓存队列等待核心线程执行。
4:maximumPoolSize > curSize > corePoolSize大小,缓冲队列已满;创建临时线程(超时都会被释放)
5:curSize >= maximumPoolSize,则执行拒绝策略。
注:可以想象成银行排队;
取钱的人==线程;
固定开启的窗口==核心线程大小;
等待区等待的人==缓冲队列;
应急窗口+核心窗口==最大线程
一人取钱,常开窗口有空闲的,直接到空闲窗口办理业务;若是没有空闲的,坐在等候区等待;若是等候区也满了,银行立即开启应急窗口,等待的人也可以去应急窗口办理业务;若应急窗口同样满了;银行保安开始执行应急策略,劝退后来者。业务办理完了,人员渐渐没了,应急窗口发现好长时间没有客户来自己窗口,则应急窗口关闭。那些核心窗口无论有没有人都会一直开着。
四:拒绝策略
1:new ThreadPoolExecutor.AbortPolicy()
默认拒绝策略,拒绝任务并抛出任务
2:new ThreadPoolExecutor.CallerRunsPolicy()
使用调用线程的线程直接运行,即哪来的回到哪里;比如main线程开启一个线程调用,若是线程池触发了该拒绝策略,则,这个线程会回归到main线程中执行。
3:new ThreadPoolExecutor.DiscardPolicy()
直接拒绝任务,不抛出错误。
4:new ThreadPoolExecutor.DiscardOldestPolicy()
触发拒绝策略,只要还有任务新增,一直会丢弃阻塞队列的最老的任务,并将新的任务加入
五:代码实现
package com.test.thread;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,15,5, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());
/**
* 如下:12个任务使用多线程执行
* 则运行的结果:会有9个线程去执行:5+(12-5-3)=9
* 解释:
* 12个任务,超出核心线程但未达到最大线程
* 因此除去核心线程执行5个任务
* 加上队列中的等待执行的3个任务,
* 还有4个任务没有执行12-5-3=4
* 这时候线程池会开启额外的线程去执行剩余的任务
* 因此还会开启4个额外线程(5+4<15:不能超过最大线程,超过最大线程,则会执行拒绝策略)
* 因此执行的任务的线程共计:核心线程5+额外线程4=9个线程;
*/
for(int i=1;i<=12;i++){
final int temp = i;
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
executor.shutdown();
}
}
六:线程工具
Exectors:jdk提供的线程池工具类。对上面的ThreadExectorPool进行封装。
1:newFixedThreadPool(int nThreads)
public static ExecutorService newFixedThreadPool(int nThreads)
一种线程数量固定的线程池,当线程处于空闲状态时,他们并不会被回收,除非线程池被关闭。当所有的线程都处于活动状态时,新的任务都会处于等待状态,直到有线程空闲出来
2:newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor()
创建单个线程。它适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景。
3:newCachedThreadPool()
public static ExecutorService newCachedThreadPool()
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
存在三个任务a,b,c。线程池创建线程1用于执行任务a,如果a任务执行完后,任务c尚未执行,则线程1将用于执行任务c。
4:newScheduledThreadPool(int corePoolSize)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行, 支持执行定时性或周期性任务。
5:newWorkStealingPool(int parallelism)
public static ExecutorService newWorkStealingPool(int parallelism)
顾名思义,它基于 工作窃取算法,其中任务可以生成其他较小的任务,这些任务将添加到并行处理线程的队列中。如果一个线程完成了工作并且无事可做,则可以从另一线程的队列中“窃取”工作。
真正意义上的并行,只要有活着的线程,就会让空闲的CPU内核去执行。而不是传统多线程的逻辑意义上的并行。