ThreadPoolExecutor在Java多线程编程中非常重要,JDK提供的线程池,最终都会通过new ThreadPoolExecutor(...)来创建线程池
ThreadPoolExecutor如下
public ThreadPoolExecutor(int corePoolSize, // 1
int maximumPoolSize, // 2
long keepAliveTime, // 3
TimeUnit unit, // 4
BlockingQueue<Runnable> workQueue, // 5
RejectedExecutionHandler handler // 6
) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
参数1: 核心线程数
参数2: 最大线程数(容易误解,下面详细说明)
参数3: 空闲线程存活时间
参数4: 对于参数3的时间单位
参数5: 保存线程的队列
参数6: 当被提交的线程被拒绝执行时调用的方法
好了,参数的意思大致说了下,不过可能有些大白话。下面来举例说明下
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, // 核心线程数
2, // 最大线程数
60, // 存活时间
TimeUnit.SECONDS, // 单位秒
new ArrayBlockingQueue<>(4), // 工作队列,容量是4
new B_MyRejected() // 拒绝策略
);
B_Task t1 = new B_Task(1, "任务1");
B_Task t2 = new B_Task(2, "任务2");
B_Task t3 = new B_Task(3, "任务3");
B_Task t4 = new B_Task(4, "任务4");
B_Task t5 = new B_Task(5, "任务5");
B_Task t6 = new B_Task(6, "任务6");
B_Task t7 = new B_Task(7, "任务7");
executor.execute(t1);
executor.execute(t2);
executor.execute(t3);
executor.execute(t4);
executor.execute(t5);
executor.execute(t6);
executor.execute(t7);
B_Task耗时1秒,代码后面再给出
上面的代码,首先创建了一个线程池executor
然后,将任务t1到t7都扔进线程池执行
那么,
一:在executor.execute(t1);之后,会创建一个线程出来并且执行t1。由于核心线程数量=1,那么改线程不会被销毁,而是一直缓存在线程池中。
二:由于t1需要1秒才能执行完成,并且核心线程数量=1,那么当t1执行完成之前,剩下的任务有4个就会进入工作队列等待执行(因为工作队列容量是4),这样t2,t3,t4,t5进入工作队列等待t1执行完毕
三:剩下t6,t7,这时,工作队列已被占满,无法再缓存任务,但是,最大线程数=2,而t1已经占用了一个线程数,还剩下一个线程数可以使用,那么这时,会再次new一个新的线程执行t6(这里有种咸鱼翻身的感觉,明明后加入,却比2,3,4,5先执行)
四:好吧,t6咸鱼翻身了,t7呢?t7就没有那么好运了,会被拒绝执行,不过被拒绝的同时也会进入拒绝策略B_MyRejected(),执行rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法中的自定义代码
五:当所有任务运行结束后,本来存在的2个线程(最大线程数=2),会回收一个,值留下1个缓存(因为核心线程数=1)
最终:写完了
剩余代码片段如下:
package thread_demo_02;
/**
* @Author: yesiming
* @Platform: Mac
* @Date: 5:46 PM 2019/10/12
*/
public class B_Task implements Runnable {
private int id;
private String name;
public B_Task(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(getId() + " ->线程执行...开始...");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getId() + " ->线程执行...结束...");
}
}
package thread_demo_02;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Author: yesiming
* @Platform: Mac
* @Date: 6:42 PM 2019/10/12
*/
public class B_MyRejected implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("自定义拒绝策略...");
System.out.println("当前被拒绝的任务是:" + ((B_Task)r).getId());
// TODO: 下面就可以
// 1. 记录日志,在非高峰期,再根据日志从新处理(推荐)
// 2. 放到缓存里,下次在执行(不推荐,Queue满了才会拒绝执行,此时应该考虑到了内存容量问题,那么也就不要往内存里放数据了)
// 4. 其他做法,我就不再叨叨了
}
}
网友评论