博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何解决Java线程池队列过饱问题
阅读量:2440 次
发布时间:2019-05-10

本文共 3498 字,大约阅读时间需要 11 分钟。

0?wx_fmt=png

public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue
());}

Java的Executors框架提供的定长线程池内部默认使用LinkedBlockingQueue作为任务的容器,这个队列是没有限定大小的,可以无限向里面submit任务。当线程池处理的太慢的时候,队列里的内容会积累,积累到一定程度就会内存溢出。即使没有内存溢出,队列的延迟势必会变大,而且如果进程突然遇到退出信号,队列里的消息还没有被处理就被丢弃了,那必然会对系统的消息可靠性造成重大影响。

那如何解决线程池的过饱问题呢?从队列入手,无外乎两种方法

  1. 增加消费者,增加消费者处理效率

  2. 限制生产者生产速度

增加消费者就是增加线程池大小,增加消费者处理效率就是优化逻辑处理。但是如果遇到了IO瓶颈,消费者处理的效率完全取决于IO效率,在消费能力上已经优化到了极限还是处理不过来怎么办?或者系统突然遇到用户高峰,我们所配置的线程池大小不够用怎么办?

这时候我们就只能从生产者入手,限制生产者的生产速度。那如何限制呢?

public LinkedBlockingQueue(int capacity) {        if (capacity <= 0) throw new IllegalArgumentException();        this.capacity = capacity;        last = head = new Node
(null); }

LinkedBlockingQueue提供了capacity参数可以限制队列的大小,当队列元素达到上线的时候,生产者线程会阻塞住,直到队列被消费者消费到有空槽的时候才会继续下去。这里似乎只要给队列设置一个大小就ok了。

但是实际情况并不是我们所想的那样。

public void execute(Runnable command) {        int c = ctl.get();        if (workerCountOf(c) < corePoolSize) {            if (addWorker(command, true))                return;            c = ctl.get();        }        if (isRunning(c) && workQueue.offer(command)) {  # here            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); # here    }

翻开源码可以发现生产者向队列里塞任务用的方法是workQueue.offer(),这个方法在遇到队列满时是不会阻塞的,而是直接返回一个false,表示抛弃了这个任务。然后生产者调用reject方法,进入拒绝处理逻辑。

接下来我们看看这个reject方法到底干了什么

final void reject(Runnable command) {        handler.rejectedExecution(command, this);}
0?wx_fmt=jpeg

我们看到JDK默认提供了4中拒绝策略的实现。

  1. AbortPolicy 默认策略,抛出RejectedExecutionException异常

  2. CallerRunsPolicy 让任务在生产者线程里执行,这样可以降低生产者的生产速度也不会将生产者的线程堵住

  3. DiscardPolicy 直接抛弃任务,不抛异常

  4. DiscardOldestPolicy 直接抛弃旧任务,不抛异常

一般比较常用的是CallerRunPolicy,比较优雅的解决了过饱问题。如果你觉得这种方式不那么优雅的话,还可以使用下面的几种方式。这几种方式都是通过处理RejectExecution来实现生产者的阻塞的目的。

public class BlockWhenQueueFullHandler implements RejectedExecutionHandler {    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {        pool.getQueue().put(new FutureTask(r));    }}

这种方案是使用put方法会阻塞生产者线程的原理达到了目的。

public class BlockWhenQueueFull implements RejectedExecutionHandler {    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {        try {            long waitMs = 10;            Thread.sleep(waitMs);        } catch (InterruptedException e) {}        executor.execute(r);    }}

这种方案显而易见,用sleep达到了阻塞的目的。

public class BoundedExecutor {    private final Executor exec;    private final Semaphore semaphore;    public BoundedExecutor(Executor exec, int bound) {        this.exec = exec;        this.semaphore = new Semaphore(bound);    }    public void submitTask(final Runnable command)            throws InterruptedException, RejectedExecutionException {        semaphore.acquire();        try {            exec.execute(new Runnable() {                public void run() {                    try {                        command.run();                    } finally {                        semaphore.release();                    }                }            });        } catch (RejectedExecutionException e) {            semaphore.release();            throw e;        }    }}

这种方案是通过信号量的大小都限制队列的大小,也不需要特别限定executor队列大小了

同样的原理还可以使用wait/notifyAll机制来达到一样的目的。

转载地址:http://ltbqb.baihongyu.com/

你可能感兴趣的文章
亚洲开源航母呼之欲出 目标瞄向Novell与红帽(转)
查看>>
正版化:水到渠成?预装Windows对Linux无打压(转)
查看>>
Red Hat并购JBoss 谁将受创?(转)
查看>>
基于IBM大型主机,Linux开辟意大利旅游新天地(转)
查看>>
一些Linux试题(经典!!)(转)
查看>>
优化MySQL数据库性能的八大“妙手”(转)
查看>>
小心:谁动了你的注册表(转)
查看>>
Unix/BSD/Linux的口令机制初探(转)
查看>>
福布斯:Sun下场本可避免 老CEO不听劝(转)
查看>>
清华紫光笔记本和PC电脑预装LINUX操作平台(转)
查看>>
根据什么选择一套适合自己的linux系统?(转)
查看>>
新型威盛电脑处理器亮相国内市场(转)
查看>>
戴尔将在法国推出Linux笔记本(转)
查看>>
近9成盗版Office用户称愿投奔开源(转)
查看>>
MySQL购InnoDB不敌甲骨文宣布开放数据引擎(转)
查看>>
银行监会选红旗Linux建设公文传输系统(转)
查看>>
红旗支撑国家外汇管理局网上核销系统(转)
查看>>
实例讲解密码破解以及抗击手段介绍(转)
查看>>
网上交易中帐号和密码被盗的解决途径(转)
查看>>
Java线程总结(转)
查看>>