java线程池之ThreadPoolExecutor(二):任务入队列和任务丢弃

一、关于任务入队列

在上一篇【java线程池之ThreadPoolExecutor(一)】中,

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(5));
定义线程池用的是ArrayBlockingQueue,该队列是有容量限制的。譬如这里,队列里面同时只能存在5个任务排队等候。

其官方解释如下:

ArrayBlockingQueue<E>:一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。 
这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。 

除了ArrayBlockingQueue队列,java还为我们提供了其它的队列:


LinkedBlockingQueue<E>  :一个基于已链接节点的、范围任意的 blocking queue。此队列按 FIFO(先进先出)排序元素。队列的头部 是在队列中时间最长的元素。队列的尾部 是在队列中时间最短的元素。新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。

PriorityBlockingQueue<E>:一个无界阻塞队列,它使用与类 PriorityQueue 相同的顺序规则,并且提供了阻塞获取操作。虽然此队列逻辑上是无界的,但是资源被耗尽时试图执行 add 操作也将失败(导致 OutOfMemoryError)。此类不允许使用 null 元素。依赖自然顺序的优先级队列也不允许插入不可比较的对象(这样做会导致抛出 ClassCastException)。 

DelayQueue<E extends Delayed>:Delayed 元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部 是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且 poll 将返回 null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于等于 0 的值时,将发生到期。即使无法使用 take 或 poll 移除未到期的元素,也不会将这些元素作为正常元素对待。例如,size 方法同时返回到期和未到期元素的计数。此队列不允许使用 null 元素。 

......

等等不一一列举,感兴趣的同学自己去查看api文档。

现在把上一篇【java线程池之ThreadPoolExecutor(一)】中的创建线程池的代码修改如下:

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
运行结果如下:

正在执行task==2
正在执行task==4
正在执行task==1
正在执行task==3
正在执行task==5
=========第1次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:5
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:10
已经执行完成的任务数目:0
=========第1次检测end===========
=========第2次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:5
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:10
已经执行完成的任务数目:0
=========第2次检测end===========
=========第3次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:5
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:10
已经执行完成的任务数目:0
=========第3次检测end===========
线程:task==2执行完毕
正在执行task==6
=========第4次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:5
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:9
已经执行完成的任务数目:1
=========第4次检测end===========
=========第5次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:5
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:9
已经执行完成的任务数目:1
=========第5次检测end===========
线程:task==3执行完毕
正在执行task==7

........

线程:task==9执行完毕
正在执行task==13
线程:task==11执行完毕
正在执行task==14
线程:task==7执行完毕
正在执行task==15
=========第11次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:5
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:10
=========第11次检测end===========
线程:task==13执行完毕
=========第12次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:4
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:11
=========第12次检测end===========
线程:task==14执行完毕
=========第13次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:3
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:12
=========第13次检测end===========
线程:task==6执行完毕
线程:task==12执行完毕
=========第14次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:1
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:14
=========第14次检测end===========

......

=========第20次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:1
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:14
=========第20次检测end===========
线程:task==15执行完毕
=========第21次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:0
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:15
=========第21次检测end===========

你会发现活跃线程数始终不会超过5个,因为这时候队列的容量没有限制,不管多少任务来了,排队便是,线程池中始终由5个核心线程重复执行。

二. 线程池中的任务丢弃

对于任务丢弃,ThreadPoolExecutor以内部类的形式实现了4个策略。分别是:
CallerRunsPolicy。提交任务的线程自己负责执行这个任务。
AbortPolicy。使Executor抛出异常,通过异常做处理。
DiscardPolicy。丢弃提交的任务。
DiscardOldestPolicy。丢弃掉队列中最早加入的任务。

在调用构造方法时,参数中未指定RejectedExecutionHandler情况下,默认采用AbortPolicy。

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(5));
		 try {
			for(int i=1;i<=20;i++){//执行20个任务,但第16个任务会报异常,从而被拒绝。
			     MyTask myTask = new MyTask("task=="+i);
			     executor.execute(myTask);
			 }
		} catch (Exception e) {
			e.printStackTrace();
		}
		 //该线程用于对线程池的检测
		 new Thread(new CheckTask(executor)).start();
		 executor.shutdown();
运行结果如下所示:

java.util.concurrent.RejectedExecutionException: Task com.thread.pool.MyTask@aeea66 rejected from java.util.concurrent.ThreadPoolExecutor@19eda2c[Running, pool size = 10, active threads = 10, queued tasks = 5, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)
	at com.thread.pool.ThreadPoolTest2.main(ThreadPoolTest2.java:32)
正在执行task==2
正在执行task==4
正在执行task==11
正在执行task==13
正在执行task==15
正在执行task==1
正在执行task==3
正在执行task==5
正在执行task==12
正在执行task==14
线程:task==1执行完毕
正在执行task==6
线程:task==11执行完毕
正在执行task==7
=========第1次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:10
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:3
已经执行完成的任务数目:2
=========第1次检测end===========
=========第2次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:10
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:3
已经执行完成的任务数目:2
=========第2次检测end===========
线程:task==15执行完毕
正在执行task==8
线程:task==6执行完毕
正在执行task==9
=========第3次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:10
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:1
已经执行完成的任务数目:4
=========第3次检测end===========
从第16个任务开始就报异常被中止了。

当然我们在创建线程池的时候可以指明直接丢掉过多的任务,如下:

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(5),new ThreadPoolExecutor.DiscardPolicy());
		 try {
			for(int i=1;i<=20;i++){//执行20个任务,但第16个任务开始就会被丢弃。
			     MyTask myTask = new MyTask("task=="+i);
			     executor.execute(myTask);
			 }
		} catch (Exception e) {
			e.printStackTrace();
		}
		 //该线程用于对线程池的检测
		 new Thread(new CheckTask(executor)).start();
		 executor.shutdown();
请注意,线程池的构造方法现在多出了一个参数new ThreadPoolExecutor.DiscardPolicy()。运行结果如下:

正在执行task==2
正在执行task==4
正在执行task==11
正在执行task==13
正在执行task==15
正在执行task==1
正在执行task==3
正在执行task==5
正在执行task==12
正在执行task==14
线程:task==14执行完毕
正在执行task==6
=========第1次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:10
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:4
已经执行完成的任务数目:1
=========第1次检测end===========
线程:task==3执行完毕
正在执行task==7
线程:task==15执行完毕
正在执行task==8
=========第2次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:10
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:2
已经执行完成的任务数目:3
=========第2次检测end===========
=========第3次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:10
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:2
已经执行完成的任务数目:3
=========第3次检测end===========
.......

线程:task==7执行完毕
=========第12次检测start===========
线程池中核心线程数:5
线程池中活跃线程数目:0
线程池中允许的最大线程数目:10
队列中等待执行的任务数目:0
已经执行完成的任务数目:15
=========第12次检测end===========
注意:这里找不到第16个任务执行情况,也就是说从第16个任务开始就被丢弃了,也不会报错。
还有其它任务丢弃策略,请自行查看api文档研究!

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

御前两把刀刀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值