从线程池执行任务说起,看看线程池中的线程什么时候关闭

前两天看到一个问题,是关于核心线程池中  非核心线程  过期时间问题的。嗯,顺便推荐一下大佬博客 面试官一个线程池问题把我问懵逼了

大概说下问题(类似),就是新建一个线程池,核心线程是3个,最大线程数是6个,阻塞队列是12,过期时间是20s, 假设每个线程处理完一个任务需要1s。如果一次性来了18 个任务,也就是线程全开,队列塞满,那么:3s之后(也就是任务搞完了),每2s来一个任务,20s之后非核心线程会关闭么?

乍一看到这个问题,我也是懵了,非核心线程不是到点了就关掉了么?哦,你任务处理完了,我还有6个线程,每2s来一个,处理一个要1s,就这事?我一个线程就能搞定啊,还要啥6个线程?非核心线程可以关了啊!还在那干啥子?浪费性能!

但是,事实真的如此么?我们来看一看哈。

还记得非核心线程的处理逻辑:

 // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))&& (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }

很明显,首先 ,就是判断如果当前线程数量超过核心线程或者允许核心线程关闭的话,在获取阻塞队列中数据的时候,就会添加一个过期时间,否则,就一直等待获取。  

看代码!瞅瞅,逻辑多么的清晰,就是超时就获取失败然后跳出循环,在runWork中的finally方法,然后这个线程就game over了!光荣退休!

看到这里,是不是,隐隐感觉,有那么一点点不对劲?这个线程获取任务,哪个先,哪个后?是就让某个线程直接去处理任务么?其他的线程直接下岗?

想要知道里面的逻辑,去看看呗,阻塞队列的源码不是在那么?

阻塞队列的话,线程池中,我比较喜欢使用 LinkedBlockingQueue,存取都是那么的方便,使用其他的阻塞队列也可以,大同小异的,待会进入源码,你就明白了,哈哈。

秀源码:

精彩总在后面:

 public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        E x = null;
        int c = -1;
        long nanos = unit.toNanos(timeout);
        final AtomicInteger count = this.count;
		// 加锁 ReentrantLock
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            while (count.get() == 0) {
			     // 超时控制
                if (nanos <= 0)
                    return null;
				//  条件队列,阻塞,线程在这里就卡这了,除非来新的任务或者超时
				//  private final Condition notEmpty = takeLock.newCondition(); 
                nanos = notEmpty.awaitNanos(nanos);
            }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

咦,我们发现,线程是park在nanos = notEmpty.awaitNanos(nanos); 这行代码了,不过这里还是看不出来,线程获取任务的先后顺序啊!

哎,没办法,只能在进入nanos = notEmpty.awaitNanos(nanos); 里面的源码java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject#awaitNanos看看吧。

到了这里,大家是不是已经十分熟悉了?这不就是AQS里面的条件队列和同步队列么?在这个条件队列里面,线程是怎么排序的,是随机的么?显然不是!

新来的线程明显被放在了最后!等待时间是一样的,后面来的又排在了后面,好了,只能顺序排队了.

到了这里,我们可以知道,哦!原来线程池中的线程是顺序获取任务的啊。(其他阻塞队列几乎也一样,都是用的ReentrantLock)

那么,回到我们的题目:

3s之后,任务执行完了,六个线程卡在这行代码:

Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();

而且,20s才会过期哦。

看一下上图,总共有6个线程,每个线程等待2s,执行1s,也就是每个线程占据3s的时间段,当6个线程执行一圈之后, 猛然发现,3*6 = 18 < 20 ! 好像还是没有一个线程关闭,而这时候,又有任务来了!

走了一圈线程也没关掉啊,再来一圈又是18s,线程还是关不掉!如果这么持续下去,也就是说,这里的非核心线程永远都不会关闭!

所以啊,线程池中的线程,不见得是到点就关闭哈,嘿嘿!

 好啦,啰嗦了这么多,不就是想知道啥时候非核心线程关闭?老铁们,一起来总结下?

(等待时间+执行时间)*线程个数  > 过期时间  

只有满足这种条件,非核心线程才能关闭,可不是到了时间非核心线程就会关闭,嘿嘿!

如有错误,还请斧正!

No sacrifice,no victory!

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笔下天地宽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值