四个任务拒绝策略该用哪一个?
在前面的课程中,我们学习了提交任务和取消任务,这一节我们来学习,当提交的任务被拒绝时该怎么办?线程池给我们提供了4个任务拒绝策略,
也就是4个解决办法,这4个策略分别对应4个类,
而且他们还以内部类的形式存在于所谓的threadPoolExecutor中。不仅如此,他们还实现了同一个接口rejectExecutionhandler,拒绝执行处理器,
它里面只有一个方法,rejectedExecution,拒绝任务的代码就写在这个方法里面。
该方法有两个参数,一个是被拒绝的任务,还有一个是线程池。如果我们想自定义处理器,但还没想好怎么处理被拒绝的任务,那么你可以抛一个拒绝执行异常,接下来我们来依次了解这个词的策略。
首先来看第一个策略,discardPolicy,该策略是直接丢弃任务,
从源码中我们可以看到 rejectedExecution型方法体是空的,
什么都没做,等同于将任务丢弃。下面我们来演示该策略。在演示之前我把演示思路说一下,为了限制数量和任务数量可控,我们采用threadPoolExecutor的这种原生方式来创建线程值,线程池中的限制数量为1,
当我们提交一个任务时,它就会被占满,任务队列大小也是一,
再提交一个任务时,他也会被沾满。
如此一来,如果我们再提交任务时,其中已经没有可工作的线程了,
队列中也没有多余的空位任务,只有被拒绝的命运。
按照这个思路我们来编写代码,编写一个runnable波任务task,定一个int类型的属性index,用于给任务编号,这样可以看到哪个任务被执行,哪个任务被拒绝,重载构造的方法用于初始化index,任务内容是输出当前线程名称和任务编号。
接下来我们来执行该任务,使用ThreadPoolExecutor创建线程值,核心限制数为一,最大限制数也是一,说明线程池中只能存在一个线程,空闲线程存活时间为0,时间单位为秒,说明空闲线程不会被销毁任务,对你采用linkedblockingQueue队列长度为一,说明队列中只能存放一个任务任务,拒绝策略,采用discardPolicy,直接丢弃被拒绝的任务,向线程池中提交三个任务,三个任务的编号分别是123使用try-catch捕获拒绝执行异常。
写上finally代码块,在finally代码块中关闭线程池,
至此main方法编写完成,整个例子也编写完成。执行程序观察执行结果,从执行结果来看,程序输出一二
说明第三个任务没执行被丢弃了。测试结果符合预期。
callerRunsPolicy
看完了第一个策略,再来看下一个策略,callerRunsPolicy。该策略是使用调用者线程直接执行被拒绝的任务。
从源码中可以看到,当线程池没关闭时,直接调用任务的run方法,而这行代码就是使用调用者的线程执行的,线程池执行任务的途径只有通过execute和submit的方法,所以不会线程池执行的。
下面我们来演示该策略还是沿用之前的例子,将策略由discardPolicy改为callerRunsPolicy。例子改写完成
再来执行程序,看看从执行结果来看,程序输出123,并且三是main线程打印的,
事实证明被拒绝的任务的确是使用调用者的线程执行的,看完了第二个策略。
abroadPolicy
再来看第三个策略,abortPolicy该策略是默认的拒绝策略,
它的做法是抛出任务拒绝异常。从源码中可以看到方法体中只有一段抛异常的代码,
下面我们来演示该策略,继续沿用之前的例子,将策略由callerRunsPolicy改为abroadPolicy,因此改写完成,再来执行程序看看。
从执行结果来看,程序输出1和2,还有拒绝执行异常测试结果符合预期
discardOldestPolicy
。接下来我们再来看看最后一个策略,discardOldestPolicy。该策略是丢弃处于任务队列头部的人物,添加被拒绝的任务,
从源码中可以看到,当线程池没被关闭时,调用任务队列的poll方法,
该方的作用是移除队列头部元素,也就是移除队列头部任务,然后将被拒绝的任务重新提交给线程池。
为了演示该策略,我们得改一改之前的任务队列容量,将任务队列容量加一,这样提交的第二个任务就会在队列的头部,第三个任务自然就在第二个任务的后面,此时任务队列被占满,
在提交任务时会将第二个任务移出队列,第三个任务成为头部任务。
第四个任务添加成功,
按照这个改动来编写代码还是之前的例子。首先将任务队列容量由一改为二,然后将测量由abroadPolicy改为discardOldestPolicy。接着再提交1个任务,一共是提交了4个任务,例子改写完成执行程序看看。
从执行结果来看,程序输出134,
果然第二个任务被移出了队列。
总结
最后来总结一下本节内容,本节介绍4个任务拒绝策略,它们的作用这里就不再赘述了。在实际开发中abroadPolicy这种策略用的最多,我一般也是用它。当然了你可以制定任务,拒绝策略。
附录:
笔记完整文本:
在前面的课程中,我们学习了提交任务和取消任务,这一节我们来学习,当提交的任务被拒绝时该怎么办?线程池给我们提供了4个任务拒绝策略,也就是4个解决办法,这4个策略分别对应4个类,而且他们还以内部类的形式存在于所谓的threadPoolExecutor中。不仅如此,他们还实现了同一个接口rejectExecutionhandler,拒绝执行处理器,它里面只有一个方法,rejectedExecution,拒绝任务的代码就写在这个方法里面。该方法有两个参数,一个是被拒绝的任务,还有一个是线程池。如果我们想自定义处理器,但还没想好怎么处理被拒绝的任务,那么你可以抛一个拒绝执行异常,接下来我们来依次了解这个词的策略。 首先来看第一个策略,discardPolicy,该策略是直接丢弃任务,从源码中我们可以看到 rejectedExecution型方法体是空的,什么都没做,等同于将任务丢弃。下面我们来演示该策略。在演示之前我把演示思路说一下,为了限制数量和任务数量可控,我们采用threadPoolExecutor的这种原生方式来创建线程值,线程池中的限制数量唯一,当我们提交一个任务时,它就会被占满,任务队列大小也是一,再提交一个任务时,他也会被沾满。如此一来,如果我们再提交任务时,其中已经没有可工作的线程了,队列中也没有多余的空位任务,只有被拒绝的命运。 按照这个思路我们来编写代码,编写一个runnable波任务task,定一个int类型的属性index,用于给任务编号,这样可以看到哪个任务被执行,哪个任务被拒绝,重载构造的方法用于初始化index,任务内容是输出当前线程名称和任务编号。接下来我们来执行该任务,使用ThreadPoolExecutor创建线程值,核心限制数为一,最大限制数也是一,说明线程池中只能存在一个线程,空闲线程存活时间为0,时间单位为秒,说明空闲线程不会被销毁任务,对你采用linkedblockingQueue队列长度为一,说明队列中只能存放一个任务任务,拒绝策略,采用discardPolicy,直接丢弃被拒绝的任务,向线程池中提交三个任务,三个任务的编号分别是123使用try-catch捕获拒绝执行异常。 写上finally代码块,在finally代码块中关闭线程池,至此main方法编写完成,整个例子也编写完成。执行程序观察执行结果,从执行结果来看,程序输出一二说明第三个任务没执行被丢弃了。测试结果符合预期。看完了第一个策略,再来看下一个策略,callerRunsPolicy。该策略是使用调用者线程直接执行被拒绝的任务。从源码中可以看到,当线程池没关闭时,直接调用任务的run方法,而这行代码就是使用调用者的线程执行的,线程池执行任务的途径只有通过execute和submit的方法,所以不会线程池执行的。 下面我们来演示该策略还是沿用之前的例子,将策略由discardPolicy改为callerRunsPolicy。例子改写完成再来执行程序,看看从执行结果来看,程序输出123,并且三是main线程打印的,事实证明被拒绝的任务的确是使用调用者的线程执行的,看完了第二个策略。再来看第三个策略,abortPolicy该策略是默认的拒绝策略,它的做法是抛出任务拒绝异常。从源码中可以看到方法体中只有一段抛异常的代码,下面我们来演示该策略,继续沿用之前的例子,将策略由callerRunsPolicy改为abroadPolicy,因此改写完成,再来执行程序看看。 从执行结果来看,程序输出1和2,还有拒绝执行异常测试结果符合预期。接下来我们再来看看最后一个策略,discardOldestPolicy。该策略是丢弃处于任务队列头部的人物,添加被拒绝的任务,从源码中可以看到,当线程池没被关闭时,调用任务队列的poll方法,该方的作用是移除队列头部元素,也就是移除队列头部任务,然后将被拒绝的任务重新提交给线程池。为了演示该策略,我们得改一改之前的任务队列容量,将任务队列容量加一,这样提交的第二个任务就会在队列的头部,第三个任务自然就在第二个任务的后面,此时任务队列被占满,在提交任务时会将第二个任务移出队列,第三个任务成为头部任务。 第四个任务添加成功,按照这个改动来编写代码还是之前的例子。首先将任务队列容量由一改为二,然后将测量由abroadPolicy改为discardOldestPolicy。接着再提交1个任务,一共是提交了4个任务,例子改写完成执行程序看看。从执行结果来看,程序输出134,果然第二个任务被移出了,队列。最后来总结一下本节内容,本节介绍4个任务拒绝策略,它们的作用这里就不再赘述了。在实际开发中abroadPolicy这种策略用的最多,我一般也是用它。当然了你可以制定任务,拒绝策略。