先上的简单封装的类
public class TaskExecutor { private static final int DEFAULT_TASK_QUEUE_LEN = 200; private static final long DEFAULT_KEEP_ALIVE_TIME = 10 * 1000; private BlockingQueue<Runnable> taskQueue; private ThreadPoolExecutor executor; private static TaskExecutor instance; public static TaskExecutor getInstance(){ if(instance == null) instance = new TaskExecutor(); return instance; } private TaskExecutor() { this.taskQueue = new ArrayBlockingQueue(DEFAULT_TASK_QUEUE_LEN); } public void setPoolSize(int poolSize){ if(this.executor == null){ this.executor = new ThreadPoolExecutor(poolSize,poolSize,DEFAULT_KEEP_ALIVE_TIME,TimeUnit.SECONDS,this.taskQueue); }else{ this.executor.setCorePoolSize(poolSize); this.executor.setMaximumPoolSize(poolSize); } } public void execute(Runnable task) throws InterruptedException { while (taskQueue.remainingCapacity() <= 0) { Thread.sleep(1); } executor.execute(task); } public void waitQueueClean() throws InterruptedException { while(taskQueue.remainingCapacity() < DEFAULT_TASK_QUEUE_LEN){ Thread.sleep(1);} } }
这个类简单的来说是用来添加多个任务,
CorePoolSize和MaximumPoolSize设置同样大小。添加任务是如果队列已满则等待直到队列有空闲。
现在碰到这样的情况,代码中在向线程池中添加任务时发现偶尔有任务没被执行,但一直找不到原因。求大神帮忙分析一下到底是什么问题。
调用代码如下
public class classA { private TaskExecutor task; public classA(){ this.task = TaskExecutor.getInstance(); } public void run() throws InterruptedException { for(int i=0;i<10000;i++){ this.task.setPoolSize(RandomNumber); for(int j=0;j<10000;j++){ this.task.execute(new Runnable() { @Override public void run() { //要执行的任务 } }); } } } }
感激不尽
因为ThreadPoolExecutor在execute时是使用BlockingQueue的offer方法,其对象的派生类在实现这个方法时都是在offer失败后(队列已满)调用ThreadPoolExecutor的RejectedExecutionHandler
这里有两个方法:
1.实现自己的队列,在offer方法中阻塞住写入队列操作
2.使用ThreadPoolExecutor的重载构造函数传入一个自己实现的RejectedExecutionHandler,比如直接在rejectedExecution中起线程跑对应的runnable
队列已满时我会 while循环直到队列中有空时才会execute的,所以应该不会调用RejectedExecutionHandler吧?
public void execute(Runnable task) throws InterruptedException { while (taskQueue.remainingCapacity() <= 0) { Thread.sleep(1); } executor.execute(task); }
@找回初心: 你这种线程不安全判断没意义。
@Daniel Cai: 所以应该怎么改呢。。
@找回初心: 不是给了你两种方案了么?
@Daniel Cai: 我是在主线程中循环添加任务到线程中,不是多线程添加任务,应该和线程不安全没关系吧?
@找回初心: 最后任务都会堆到queue中,而queue会被多个线程来进行消耗,你说这里安全么?
最简单的方式自己去实现queue,内部把offer方法换成其他queue的add方法(阻塞式)
@Daniel Cai: 那其实只要任务添加到Queue中,肯定会被执行的是吧?
public void execute(Runnable task) throws InterruptedException { while (taskQueue.remainingCapacity() <= 0) { Thread.sleep(1); } executor.execute(task); }
这段代码就算有多个线程在消费Queue,也不会影响主线程往里面添加任务吧?主要是现在不确定任务是否被加入到Queue中。
@找回初心: 是啊,因为blockingqueue在线程池中提供的就是offer方法,offer返回false了就被reject了。
@找回初心: 多个线程在消费,那么taskQueue.remainingCapacity()读取到的是在那一刻的数据,没有东西确保后面你真正的在execute(加到队列时)这个还是>0的。
@Daniel Cai: 但是只有这个方法可以添加任务,而且是在主线程中循环添加任务的,也不存在多线程添加任务。所以taskQueue.remainingCapacity()读到数据是什么,在executor.execute(task)时,只会更小,不会变大吧?
@找回初心: 是,刚回过头看了下你的调用代码。
其中有点我不知道会不会出问题,建议你先改下
for(int i=0;i<10000;i++){ this.task.setPoolSize(RandomNumber); for(int j=0;j<10000;j++){
中间这个setPoolSize的调用
@Daniel Cai: setPoolSize是业务需求,因为任务里有调用第三方接口的代码,对不同的一组循环要控制并发数量,所以要设置线程数量。如果线程中的任务在执行的话,设置线程数应该不会有其它影响的。现在就是不知道什么情况下会使任务丢失