线程池之深度了解-ThreadPoolExecutor


代码的世界和现实的世界还真是像,解决办法也真一样。


最近遇到个交通阻塞的问题。


场景:

服务A 提供了两个接口,get和delete


问题:
两个接口共享一个出口(如同时最多有20个连接),当其中一个接口响应慢的时候,会把几乎全部的出口带宽占完,这样会导致原本正常的get接口得不到响应,客户端在调用的时候,总是连接不上。

看起来其实有点像家用的路由器似的,3个人共享上网,当其中一个人占用了很大的带宽的时候,会导致另外连个人不能正常上网。


解决办法
也有点像路由器似的,可以根据不同的ip进行限制带宽。
服务A 也可以根据不同的接口限制多少的最大连接数


代码还原
由于项目用到了netty,里面有个executionhandler的概念,大意也就是用异步的方式(线程池)执行比较耗时的业务代码。


executeThreadPool = new ThreadPoolExecutor(minThreadPoolSize,//8
maxThreadPoolSize, 0L, TimeUnit.MILLISECONDS, // 200
new LinkedBlockingQueue<Runnable>(maxQueueBlockSize)); //1000


初步看起来感觉没什么问题,但真正运行起来后,会发现当并发数达到 20 个时候,发现进入业务代码里面的线程也就只有8个,而其余的12个是放在了queue里面了,等待核心线程去执行。
这就是问题所在,和我们的初衷并不一样(最多启动200个线程去执行客户的请求,多的时候在放到队列里面等待执行)


还是继续跟到ThreadPoolExecutor里面看吧


之前简单的写过
http://blog.csdn.net/chaofanwei/article/details/17286619


先看以下代码,感觉sun的工程师搞的挺好玩的,也算是脑洞大开啊

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;   //29
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1; //536870911   00011111111111111111111111111111


    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS; //-536870912   11100000000000000000000000000000
    private static final int SHUTDOWN   =  0 << COUNT_BITS; //0   00000000000000000000000000000000
    private static final int STOP       =  1 << COUNT_BITS; //536870912   00100000000000000000000000000000
    private static final int TIDYING    =  2 << COUNT_BITS; //1073741824   01000000000000000000000000000000
    private static final int TERMINATED =  3 << COUNT_BITS; //1610612736   01100000000000000000000000000000


    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; } //根据int c返回表示的状态
    private static int workerCountOf(int c)  { return c & CAPACITY; }  //根据int c返回表示的线程个数
    private static int ctlOf(int rs, int wc) { return rs | wc; }  // 用指定的状态和线程个数初始化 int  c

目的就是用一个int类型的数,前面3位用来标示当前线程池的状态(RUNNING,SHUTDOWN ...),后面的29位表示当前线程池里面的线程个数。


再来看核心的接口execute方法内部实现

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { //如果当前线程数小于core线程数,则直接启动新的线程并把command当做线程的第一个任务来执行
            if (addWorker(command, true)) //启动新线程,成功的话,直接返回
                return;
            c = ctl.get();
        }
        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);
    }

上面代码①意思
试图把command塞到队列里面,看是否能赛成功,这个地方根据queue的不同又分为两种情况,LinkedBlockingQueue和SynchronousQueue


LinkedBlockingQueue
应用场景就是
Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory){
 return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
}
这种情况下,如果队列没有满的话,则直接塞到里面就返回true了,但并不会启动新的线程去执行此task,而是等待别的线程空闲下来时候再从队列里面取出来执行。
只有当此队列满了,offer返回失败,才会走到下面的else语句块里面,试图启动新的线程去执行。


一句话总结也就是优先启动corePoolSize个线程,然后多的任务放到队列里面等待corePoolSize的线程去执行,只有当队列满的时候,才会启动最多maximumPoolSize的线程去执行任务。


SynchronousQueue
应用场景就是
Executors.newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }


SynchronousQueue用一句话来说就是相当于只能装一个entry的阻塞队列,也相当于一个transfer,即只有把offer进来的entry给传递到另外一个线程的时候才返回true(即如果刚好有另外一个线程在等待接收的时候才返回true)
这种情况下就是,如果offer成功了,就表示已经有另外一个线程在执行了,如果返回失败,表示所有的线程都在忙碌,就走到了下面的else语句块里面,尝试(总是成功)启动新的线程去执行此任务。




代码里面的坑太多,需要一个一个去填平!








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值