共用四种拒绝策略分别为: AbortPolicy,CallerRunsPolicy,DiscardPolicy,DiscardOldestPolicy 它们都是 ThreadPoolExecutor 类下的静态内部类,并都实现了 RejectExecutionHandler 接口;
下面的 MyRunnable 类用来描述一个任务
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 正在执行 " + System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
1. AbortPolicy:
触发该拒绝策略时,会直接抛出 RejectedExecutionException 异常;
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 8, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.AbortPolicy());
MyRunnable runnable1 = new MyRunnable();
MyRunnable runnable2 = new MyRunnable();
MyRunnable runnable3 = new MyRunnable();
executor.execute(runnable1);
executor.execute(runnable2);
executor.execute(runnable3);
}
抛出异常之后,此时新的任务也无法继续完成了,但是在阻塞队列中存在的任务并非是新的任务;所以等待 2 秒(因为 MyRunnable 接口 重写的 run 方法 sleep 了 2 秒)之后,此时线程会继续执行阻塞队列中的任务;看下面这个情况:
Thread.sleep(3000);
System.out.println(System.currentTimeMillis());
MyRunnable runnable4 = new MyRunnable();
executor.execute(runnable4);
在上述代码的基础上,再添加以上代码,再次执行程序:
可以看到,本来应该是执行完阻塞队列的任务之后,主线程休眠 3 秒,执行阻塞队列的任务会休眠2 秒,所以主线程休眠 3 秒之后,线程应该是空闲状态,理应继续执行下面的逻辑,打印当前时间戳,并执行任务 4,但是使用的是 AbortPolicy 拒绝策略,抛出异常后,后续任务也不会执行了;
2. CallerRunsPolicy:
触发该拒绝策略时,添加的任务由添加任务的线程执行;
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 8, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.CallerRunsPolicy());
MyRunnable runnable1 = new MyRunnable();
MyRunnable runnable2 = new MyRunnable();
MyRunnable runnable3 = new MyRunnable();
executor.execute(runnable1);
executor.execute(runnable2);
executor.execute(runnable3);
}
由执行结果可以看到,第一个任务由线程池的核心线程执行, 第二个任务进入阻塞队列并等待执行,第三个任务会触发拒绝策略 CallerRunsPolicy,由添加任务的线程执行,显然此处是 main 线程添加的任务,故由 main 线程执行,核心线程经过 2 秒执行完毕后,继续执行阻塞队列中的任务;此时,如果有新任务再次添加,结果又会怎么样呢?同样添加上述代码,测试结果如下:
Thread.sleep(3000);
System.out.println(System.currentTimeMillis());
MyRunnable runnable4 = new MyRunnable();
executor.execute(runnable4);
可以看出,触发 CallerRunsPolicy 拒绝策略之后,再有新任务来时,仍然能正常执行;
3. DiscardOldestPolicy:
触发该拒绝策略时,会丢弃一个阻塞队列中最古老的任务,并将新的任务加入到阻塞队列中;
为了验证该拒绝策略,对 MyRunnable 类添加一个属性 name,并对 run 方法进行调整:
class MyRunnable implements Runnable{
private String name;
public MyRunnable(String name){
this.name = name;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 正在执行 " + this.name + " 时间 " + System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 8, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.DiscardOldestPolicy());
MyRunnable runnable1 = new MyRunnable("第一个任务");
MyRunnable runnable2 = new MyRunnable("第二个任务");
MyRunnable runnable3 = new MyRunnable("第三个任务");
MyRunnable runnable4 = new MyRunnable("第四个任务");
executor.execute(runnable1);
executor.execute(runnable2);
executor.execute(runnable3);
executor.execute(runnable4);
Thread.sleep(3000);
System.out.println("所有任务执行完毕");
}
由代码及运行结果可以看出,首先第一个任务来时,由核心线程执行,第二个任务来时,进入阻塞队列,第三个任务来时,线程全部都不空闲,并且阻塞队列已满,故会触发拒绝策略,丢弃第二个任务,并将第三个任务放入阻塞队列,第四个任务来时,仍然会触发拒绝策略,丢弃第三个任务,并将第四个任务放入阻塞队列,之后核心线程执行完第一个任务之后,会继续执行阻塞队列中的第四个任务;
4. DiscardPolicy:
触发该拒绝策略时,会直接丢弃掉新来的任务,即直接忽略;
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 8, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.DiscardPolicy());
MyRunnable runnable1 = new MyRunnable("第一个任务");
MyRunnable runnable2 = new MyRunnable("第二个任务");
MyRunnable runnable3 = new MyRunnable("第三个任务");
executor.execute(runnable1);
executor.execute(runnable2);
executor.execute(runnable3);
Thread.sleep(3000);
System.out.println("所有任务执行完毕");
}
由代码及运行结果可以看出,首先第一个任务来时,由核心线程执行,第二个任务来时,进入阻塞队列,第三个任务来时,会触发 DiscardPolicy 拒绝策略,直接丢弃第三个任务,等待核心线程执行完第一个任务之后,会继续执行阻塞队列中的第二个任务;