java ThreadPoolExecutor 的使用和拒绝策略

ThreadPoolExecuor的构造方法

先来看一下ThreadPoolExecuor类的构造方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

参数说明:

  • corePoolSize: 线程池维护线程的最少数量
  • maximumPoolSize:线程池维护线程的最大数量
  • keepAliveTime: 线程池维护线程所允许的空闲时间
  • unit: 线程池维护线程所允许的空闲时间的单位
  • workQueue: 线程池所使用的缓冲队列
  • handler: 线程池对拒绝任务的处理策略

正式使用中一般都会设置一个最大缓冲队列容量,如果线程池满它会对继续添加的任务线程执行指定的拒绝策略,ThreadPoolExcetor 的最后一个参数指定了拒绝策略,JDK提供了四种拒绝策略:

  • AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
  • CallerRunsPolicy 策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前的被丢弃的任务。
  • DiscardOleddestPolicy策略: 该策略将丢弃最老的一个请求,也就是即将被执行的任务,并尝试再次提交当前任务。
  • DiscardPolicy策略:该策略默默的丢弃无法处理的任务,不予任何处理。

AbortPolicy策略

下面使用默认的AbortPolicy策略测试

private static ExecutorService executorService = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(Double.valueOf(10000*Math.random()).longValue());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.info("i,"+Thread.currentThread().getName());
                }
            });
        }
    }

执行结果

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.gdzy.analysis.service.impl.TagServiceImpl$1@1b9e1916 rejected from java.util.concurrent.ThreadPoolExecutor@ba8a1dc[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at com.gdzy.analysis.service.impl.TagServiceImpl.main(TagServiceImpl.java:49)
01:40:57.795 [pool-1-thread-1] INFO com.gdzy.analysis.service.impl.TagServiceImpl - i,pool-1-thread-1
01:41:00.972 [pool-1-thread-2] INFO com.gdzy.analysis.service.impl.TagServiceImpl - i,pool-1-thread-2

可以看到,直接就抛出了异常,在执行第三次的时候

自定义拒绝策略

有时候我们需要保证任务添加不会失败,并且只要被添加的任务能依次顺序执行就好了,而不需要这个添加动作立即响应,即让线程池等待池中的任务完成后再继续添加新任务,此时JDK提供的四种策略无法满足需求,需要自定义拒绝策略,修改线程池构造方法的最后一个参数,代码如下:

private static ExecutorService executorService = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(),Executors.defaultThreadFactory(),new RejectedExecutionHandler(){
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            try {
                executor.getQueue().put(r);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

将任务以阻塞的方式重新放回队列中,executor.getQueue().put(r)

执行结果

01:51:44.580 [pool-1-thread-1] INFO com.gdzy.analysis.service.impl.TagServiceImpl - i,pool-1-thread-1
01:51:44.580 [pool-1-thread-2] INFO com.gdzy.analysis.service.impl.TagServiceImpl - i,pool-1-thread-2
01:51:47.120 [pool-1-thread-1] INFO com.gdzy.analysis.service.impl.TagServiceImpl - i,pool-1-thread-1
01:51:50.679 [pool-1-thread-2] INFO com.gdzy.analysis.service.impl.TagServiceImpl - i,pool-1-thread-2
01:51:52.315 [pool-1-thread-1] INFO com.gdzy.analysis.service.impl.TagServiceImpl - i,pool-1-thread-1
01:51:58.987 [pool-1-thread-2] INFO com.gdzy.analysis.service.impl.TagServiceImpl - i,pool-1-thread-2
01:52:01.135 [pool-1-thread-1] INFO com.gdzy.analysis.service.impl.TagServiceImpl - i,pool-1-thread-1

可以看出,符合需求:让线程池等待池中的任务完成后再继续执行新任务,如果队列满就等待,利用了Queue put方法的阻塞。

(全文完)

(转载本站文章请注明作者和出处 java ThreadPoolExecutor 的使用和拒绝策略