public class ContentIndexBuilder {
/**
* 自定义执行器,该执行器具有有界的阻塞式队列,同时具有自定义的阻塞式拒绝执行执行器(也可以使用
* {@link ThreadPoolExecutor.CallerRunsPolicy},这种拒绝执行策略在任务队列满后会使任务在调用线程中执行)
*
* 这种方式可以避免任务无限制的迅速提交,同时避免任务的放弃
*/
private final static int N_THREADS = 5;
private final static int QUEUE_CAPACITY = 20;
private final ExecutorService es = new ThreadPoolExecutor(N_THREADS, N_THREADS, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(QUEUE_CAPACITY), new BlockingRejectedExecutionHandler());
public ContentIndexBuilder() {
new Thread(new Producer()).start();
}
/**
* DataBean生产线程
*
* @author hasee
*
*/
private class Producer implements Runnable {
@Override
public void run() {
while (isWorkingTime()) {
List<Task> list = getTask();
for (int i = 0; i < list.size(); i++) {
Consumer consumer = new Consumer(list.get(i));
// 通过阻塞式添加的拒绝策略,使得该方法成为阻塞方法
es.submit(consumer);
}
}
}
private List<Task> getTask() {
// TODO Auto-generated method stub
return null;
}
/**
* 阻塞式方法,直到返回true
*
* 不要递归实现,容易栈溢出
*
* @return
*/
private boolean isWorkingTime() {
while (!isTime()) {
try {
Thread.sleep(1000 * 60 * 10);
} catch (InterruptedException e) {
}
}
return true;
}
private boolean isTime() {
// TODO Auto-generated method stub
return false;
}
}
private class Consumer implements Runnable {
private final Task task;
private Consumer(Task task) {
this.task = task;
}
@Override
public void run() {
try {
// TODO deal with task
} catch (Exception e) {
}
}
}
/**
* 自定义拒绝执行策略:阻塞式地将任务添加至工作队列中
*
* @author hasee
*
*/
private class BlockingRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (!executor.isShutdown()) {
try {
// 使用阻塞方法向工作队列中添加任务
executor.getQueue().put(r);
} catch (InterruptedException e) {
executor.execute(r);
}
}
}
}
}
线程池存储任务的队列一般是有界的,那么当任务队列已满时,新的任务怎么处理呢?
这就要看线程池采用的是那种拒绝策略了。ThreadPoolExecutor中提供了四种拒绝策略,分别是:
CallerRunsPolicy:使用调用线程执行该任务
AbortPolicy:抛出异常并放弃任务
DiscardPolicy:直接放弃任务
DiscardOldestPolicy:放弃旧任务,添加该任务到任务队列
这里我们自定义了一种拒绝策略,即阻塞式添加。当任务队列已满时,调用线程会阻塞,直到任务队列有空位,添加。这种方式不会丢失任务。(其实CallerRunsPolicy也不会丢失任务)