线程池
Executor
执行提交的可运行(Runnable)任务的对象。此接口提供了一种方式将任务提交与每个任务的执行机制进行解耦,包括线程使用、调度等的详细信息。通常使用执行器而不是显式地创建线程。例如,与其为一组任务调用new Thread(new(RunnableTask()).start()
,不如使用:
/*
只需要将要执行的任务作为参数提交过来 就可以执行 具体执行细节是黑盒操作
传统线程执行者和提交者都是由同一操作者执行 线程池则将前者交给程序完成
线程池理念类似消息队列
我们提交者new RunnableTask1() 相当于MQ中的生产者 线程池 work执行类似于消费者
*/
Executor executor = anExecutor;
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());
...
但是,Executor接口并不严格要求执行是异步的。在最简单的情况下,执行器可以立即在调用方线程中运行提交的任务:
ThreadPoolExecutor
/**
*使用给定的初始参数创建新的ThreadPoolExecutor。
*
*corePoolSize – 池中要保留的线程数,即使它们处于空闲状态,除非设置了allowCoreThreadTimeOut
意思最少要保留的线程数 哪怕没有任务执行 也不会被销毁
* maximumPoolSize – 池中允许的最大线程数
*
* keepAliveTime – 当线程数大于核心时,这是多余空闲线程在终止前等待新任务的最长时间。
* 如corePoolSize =5 maximumPoolSize= 10
* 当前线程数为8 此时 8个线程无任务 当时间超过keepAliveTime多余3个就会被销毁
*
* unit–keepAliveTime参数的时间单位
*
* @param workQueue 工作队列在任务执行之前用于保留任务的队列。
* 此队列将只保存由execute方法提交的可运行runnable任务。
*
* threadFactory – 执行器创建新线程时使用的工厂
*
* handler–当执行因达到线程边界和队列容量而被阻止时使用的处理程序(拒绝策略)
* 意思就是当任务大于线程执行数 多余的任务该如何处理
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
后三个参数我们可以使用自定义的类去应用我们生产环境
线程池参数
int corePoolSize 核心线程数
线程池当中所一直维护的线程数量,如果线程池处于任务空闲期间,那么该线程也不会被回收掉(注意如果线程池数量小于corePoolSize值 执行多个任务会先创建线程执行任务超过该值才会进行复用 设值讲究 一般是cpu核心数X2附近
)
int maximumPoolSize 最大线程数
线程池中所维护的线程数的最大数量
long keepAliveTime
超过了corePoolSize的线程在经过keepAliveTime时间后如果一直处于空闲状态, 那么超过的这部分线程将会被回收掉
BlockingQueue workQueue 阻塞队列(队列的根接口)
指的向线程池所提交的任务位于的阻塞队列,它的实现有多种方式
SynchronousQueue同步队列
单位中有且只含有一个任务 当前任务被弹出下个任务才能进入 队列存在任务,新来任务时拒绝策略就会生效
ThreadFactory threadFactory 线程工厂
线程池的ThreadFactory必须实现ThreadFactory 同时重写newThread方法
Executors.DefaultThreadFactory实现了该工厂并且重写了newThread方法
线程工厂,用于创建新的线程并被线程池所管理,默认线程工厂所创建的线程都是用户线程且优先级为正常优先级(5)threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n
为线程池内的线程编号)。
Executors.DefaultThreadFactory 线程池默认使用的线程工厂
由上图可以知晓线程池的线程都是用户线程,我们在使用完线程池需要手动关闭线程,并且优先级默认设值都为5
RejectedExecutionHandler handler 拒绝执行处理器(4种拒绝策略根接口)
拒绝执行处理器(拒绝策略) 表示当前线程池中的线程都在忙于执行任务且阻塞队列也已经满了的情况下,新到来的任务该如被对待和处理 ,JDK提供了四种拒绝策略
RejectedExecutionHandler 拒绝执行处理程序(策略根接口)
无法由ThreadPoolExecutor执行的任务的处理程序。(意思就是提供线程池无法执行的任务对应的处理策略的模板)
一般JDK提供的策略不会满足我们日常开发,里面对应方法rejectedExecution
具体策略方法实现我们根据自己的实际业务情况进行重写
/**
* A handler for tasks that cannot be executed by a {@link ThreadPoolExecutor}.
*
* @since 1.5
* @author Doug Lea
*/
public interface RejectedExecutionHandler {
/**
方法,当execute无法接受任务时,ThreadPoolExecutor可能会调用该方法。
当线程或队列槽因超出其界限而不再可用时,或者在执行器shutdown关闭时,可能会发生这种情况。
*
* @param r–请求执行的可运行任务
* @param executor–尝试执行此任务的执行者(线程池对象)
* @throws RejectedExecutionException if there is no remedy
*/
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
该接口就是当线程池队列满了时新来的任务处理手段的概括,具体策略由子类进行处理,它的实现类有四种,都是ThreadPoolExecutor类当中的内部类
四种拒绝策略
1.AbortPolicy(终止策略)
这是一个处理器针对被拒绝的任务 直接抛出RejectedExecutionException异常(任务无法执行抛出异常)
/**
*这是一个处理器针对被拒绝的任务 直接抛出RejectedExecutionException异常
*
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* 构造方法
*/
public AbortPolicy() {
}
/**
* Always throws RejectedExecutionException.
* 总是抛出RejectedExecutionException
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
2.DiscardPolicy(丢弃策略)
如下图我们可以看到rejectedExecution执行拒绝的策略为空实现 什么都不做,也不抛出异常
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() {
}
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
3.DiscardOldestPolicy(丢弃旧任务策略)
一种被拒绝任务的处理程序,它丢弃(workQueue队列)最老的未被处理请求(队列最先被放进去的任务),然后调用e.execute(r)
重试执行当前任务(注意依然要走流程),除非执行器关闭,在这种情况下任务被丢弃。
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() {
}
/**
获取并忽略执行器将执行的下一个任务(如果有一个任务立即可用),
然后重试执行任务r,除非执行器关闭,在这种情况下,任务r被丢弃。
*@param r请求执行的可运行任务
*@param e试图执行此任务的执行者(线程池)
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
//线程池是否被关闭
//弹出WorkQueue队列第一个元素
e.getQueue().poll();
//将任务插到队列队尾 依然要走完整流程
e.execute(r);
}
}
}
4.CallerRunsPolicy(调用方运行策略)
被拒绝任务的处理程序,它直接由提交任务的线程来运行这个提交的任务,除非executor已关闭,在这种情况下任务被丢弃。
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() {
}
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
//直接执行
r.run();
}
}
}
内置拒绝实例使用
public class MyTest2 {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 5, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
IntStream.range(0, 9).forEach(x -> threadPoolExecutor.submit(()->{
//每个线程睡1S
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
IntStream.range(0, 2).forEach(i -> System.out.println(Thread.currentThread().getName()));
}));
/*
线程池创建的线程 只是用户线程不是一个守护线程 对于JVM来说 主线程执行完成 并且没有任何子线程正在执行
这时JVM进程就会退出(守护线程存在也会退出) 所以主线程mian就不会退出
我们在执行完任务要结束线程池线程 如shutdown shutdownNow
*/
threadPoolExecutor.shutdown();
}
}
当我们使用CallerRunsPolicy
时
DiscardPolicy策略:
会抛出如下java.util.concurrent.RejectedExecutionException 拒绝执行异常 [Running, pool size = 5, 活动线程数 = 5, 排队任务 = 3, 已完成任务 = 0]
当我们创建8个以下任务时,线程至多出现8个,当我们创建第9个任务此时最大线程数为5 阻塞队列最多为3个任务
此时 5+3 = 8-0 < 9 此时第9个任务一提交 超出最大任务数 就会执行拒绝策略AbortPolicy
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1f89ab83 rejected from java.util.concurrent.ThreadPoolExecutor@e73f9ac[Running, pool size = 5, active threads = 5, queued tasks = 3, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at com.example.demo.com.concurrecy.concurrency9.MyTest2.lambda$main$2(MyTest2.java:15)
at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)
at java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:557)
at com.example.demo.com.concurrecy.concurrency9.MyTest2.main(MyTest2.java:15)
DiscardPolicy
策略:
当使用DiscardPolicy 当任务大于8此时不会抛出异常 但是任务执行数量会减少 本例 2X9 = 18 而实际打印为 8x2 = 16有两个任务被丢弃 但是不会告知
DiscardOldestPolicy
策略:
同上 主要是移除队列最老的任务
callerRunsPolicy
策略:
此时任务不会丢弃,而是由调用方执行多余任务
pool-1-thread-5
pool-1-thread-5
pool-1-thread-1
pool-1-thread-1
main
main
pool-1-thread-3
pool-1-thread-3
pool-1-thread-4
pool-1-thread-4
pool-1-thread-2
pool-1-thread-2
pool-1-thread-5
pool-1-thread-5
pool-1-thread-1
pool-1-thread-1
pool-1-thread-3
pool-1-thread-3
Executors示例
public class MyTest1 {
public static void main(String[] args) {
/*
Executors 工厂类 提供多种方法创建各种类型线程池
(最好手动创建线程更灵活的使用线程池参数 Executors将其隐藏 实际业务我们需要根据并发量来调节线程池参数 如默认采用拒绝策略都是AbortPolicy 直接抛出异常)
如: newFixedThreadPool(3) 固定大小为3的线程池
newSingleThreadExecutor 单例线程池
newCachedThreadPool 缓冲线程池 pool会根据任务数自动分配线程执行任务 具体线程数根据任务决定
本质上底层都是实例化ThreadPoolExecutor 根据不同的线程池 传入不同的对象
*/
ExecutorService executorService = Executors.newFixedThreadPool(3);
IntStream.range(0, 10).forEach(x -> {
executorService.submit(() -> IntStream.range(0, 50).forEach(i -> System.out.println(Thread.currentThread().getName())));
});
/*
线程池创建的线程 只是用户线程不是一个守护线程 对于JVM来说 主线程执行完成 并且没有任何子线程正在执行
这时JVM进程就会退出(守护线程存在也会退出) 所以主线程mian就不会退出
我们在执行完任务要结束线程池线程 如shutdown shutdownNow
*/
executorService.shutdown();
}
}
不同线程池 使用的线程数数不同的
/*
Executors.newFixedThreadPool(3); 只有三个线程执行任务
...
pool-1-thread-2
...
pool-1-thread-1
...
pool-1-thread-3
...
*/
/*
Executors.newSingleThreadExecutor(); 只有一个线程执行任务
....
pool-1-thread-1
....
*/
/*
Executors.newCachedThreadPool(); 线程池根据任务分配多个线程执行任务
....
pool-1-thread-1
ool-1-thread-8
pool-1-thread-2
pool-1-thread-2
pool-1-thread-8
pool-1-thread-3
....
pool-1-thread-8
pool-1-thread-9
pool-1-thread-8
....
*/
对于线程池来说
Executors相当于工厂类 提供多种方法创建各种类型线程池, (但是 最好手动创建线程更灵活的使用线程池参数 Executors将其隐藏 实际业务我们需要根据并发量来调节线程池参数 如默认采用拒绝策略都是AbortPolicy 直接抛出异常)
注意事项
线程池创建的线程 只是用户线程不是一个守护线程 对于JVM来说 主线程执行完成 并且没有任何子线程正在执行这时JVM进程就会退出(守护线程存在也会退出) 所以主线程mian就不会退出我们在执行完任务要结束线程池线程 如shutdown shutdownNow
Executors不同类型线程池
Executors.newFixedThreadPool() 固定大小线程池
我们可以查看Executors.newFixedThreadPool(3);
具体代码实现 其他的线程池类似
每个线程池都对应一个重载的线程工厂可以不使用默认的Executors.defaultThreadFactory()
传入自定义的ThreadFactory工厂
默认的的线程工厂defaultThreadFactory
这个可以使用自定的
默认的拒绝策略AbortPolicy终止策略。
创建了一个核心线程数corePoolSize为3 MaximumPoolSize为3的线程keepAliveTime为0无界的队列 默认使用的策略是终止策略直接抛出异常
Executors.newCachedThreadPool() 缓存线程池
两种重载
我们看出CachedThreadPool
对应的核心线程数corePoolSize=0,最大线程数maximumPoolSize为Integer.MAX_VALUE
当线程空闲keepAliveTime=60
(缓存60S) 就会销毁线程池中的线程 采用的队列SyncronousQueue
阻塞队列 说明阻塞队列只能有且一个任务 默认的拒绝策略为abortPolicy
Executors.newSingleThreadPool() 单例线程池
我们看出CachedThreadPool
对应的核心线程数corePoolSize=1,最大线程数maximumPoolSize为1
无缓存时间 就会销毁线程池中的线程 采用的队列LinkedBlockingQueue
阻塞队列 说明阻塞队列是无界(默认)的 默认的拒绝策略为abortPolicy