Java主线程调度,ExecutorService执行任务

Java主线程调度,ExecutorService执行任务

在项目开发过程中,遇到了需要并发处理(例:调用百度API查询一批GPS的位置)的情况,但是http查询有QPS限制,因此只能指定一个固定的线程数执行。
但是Executors.newFixedThreadPool默认使用的是LinkedBlockingQueue,因此大数据量的情况下,数据全部offer到队列很容易触发OOM。
如果配置了LinkedBlockingQueue的capacity,则会触发ThreadPoolExecutor.RejectedExecutionHandler。

ThreadPoolExecutor.java

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //offer插入新任务
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            //没有可用线程时处理
            reject(command);
    }

通过查看RejectedExecutionHandler有以下实现:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 默认策略

ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务,一般是Executor.execute在主线程

看上去CallerRunsPolicy更符合要求,但是如果主线程也在执行任务,而http请求往往都有一定的响应时间。
假设某个子线程的任务已经完成了,主线程还在等待http响应就无法实时的给子程序调度任务。

因此CallerRunsPolicy也不适合。

下面试着换一下LinkedBlockingQueue,使用SynchronousQueue。

   ExecutorService executorService = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
           new SynchronousQueue<>(),
           new DefaultThreadFactory("test"),
           new ThreadPoolExecutor.AbortPolicy());
   for (int i = 0; i < 1000; i++) {
       executorService.execute(new Runnable() {
           @Override
           public void run() {
               try {
                   //模拟http请求
                   Thread.sleep(10);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       });
   }

触发了RejectedExecutionException

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task rejected from java.util.concurrent.ThreadPoolExecutor@5d20e46[Running, pool size = 5, active threads = 5, queued tasks = 0, completed tasks = 0]

显然SynchronousQueue的offer在无人消费的情况下直接返回false。
所以只要让offer阻塞就可以了。

    public static void main(String[] args) {
        ExecutorService executorService = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
                new SyncQueue<>(),
                new DefaultThreadFactory("test"),
                new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < 1000; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        //模拟http请求
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    public static class SyncQueue<T> extends SynchronousQueue<T> {

        @Override
        public boolean offer(T t) {
            try {
                //offer重试一次,成功返回true,失败返回false
                //put会一直等待
                super.put(t);
            } catch (InterruptedException e) {
                throw new RuntimeException();
            }
            return true;
        }
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值