请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

老师您好,既然Thread.start()只能调用一次。那么线程池是如何实现线程复用的呢?

正在回答 回答被采纳积分+3

1回答

悟空 2019-08-23 00:35:59

小伙伴的问题很好,你主动思考的能力很强。

原因:线程重用的核心是,线程池对Thread做了包装,不重复调用hread.start(),而是自己有一个Runnable.run(),run方法里面循环在跑,跑的过程中不断检查我们是否有新加入的子Runnable对象,有新的Runnable进来的话就调一下我们的run(),其实就一个大run()把其它小run()#1,run()#2,...给串联起来了。同一个Thread可以执行不同的Runnable,主要原因是线程池把线程和Runnable通过BlockingQueue给解耦了,线程可以从BlockingQueue中不断获取新的任务。

详细分析展开:

在ThreadPoolExecutor的execute方法,作用是添加将要执行的任务

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();
   }
   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);
}

   


分析:可以看出:ThreadPoolExecutor.execute()的功能就是:
1、将任务添加至阻塞队列workQueue,也就是workQueue.offer(command)
2、根据core和maxPool,选择是否创建Worker,也就是addWorker()

可以看出,线程复用的实现在Worker中,这里的Worker实现了Runnable接口,在重写的run方法里面执行了runWorker():

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

   

简化后的runWorker方法:


runWorker(Worker w) {
    Runnable task = w.firstTask;
    w.firstTask = null;
    while (task != null || (task = getTask()) != null) {
        try {
            task.run();
        } finally {
            task = null;
        }
    }
}

   

可以看出,这个方法的逻辑:

1、通过getTask()方法,获取待执行的任务。
2、通过task.run();执行具体的任务。
3、有while循环,会不停地执行。

在这里,我们找到了最终的实现,getTask方法从workQueue中取出了新任务,实现了复用,至此分析完毕。

注:getTask不仅可以取出任务,还可以控制线程数量,思路主要有两点:

1,如果当前活动线程数大于最大线程数或者等待超时,则进行Worker数目减少,也就是大于核心线程的就这样被销毁掉了。

2,如果当前活动线程数小于等于核心线程数,同样也是去缓存队列中取任务,但当缓存队列中没任务了,就会进入阻塞状态,直到能取出任务为止,因此这个线程是处于阻塞状态的,并不会因为缓存队列中没有任务了而被销毁。这样就保证了线程池有N个线程是活的,可以随时处理任务,从而达到重复利用的目的。


5 回复 有任何疑惑可以回复我~
  • 提问者 慕桂英1068294 #1
    谢谢老师的精彩回答,让我们再一次的深入敌军内部,扒开他们的内裤。这么迟还打扰老师休息,是我考虑不周。老师晚安。
    回复 有任何疑惑可以回复我~ 2019-08-23 00:58:26
  • 悟空 回复 提问者 慕桂英1068294 #2
    深入敌后,尖兵突击,就要选在万籁寂静的午夜,哈哈。
    回复 有任何疑惑可以回复我~ 2019-10-08 15:45:23
问题已解决,确定采纳
还有疑问,暂不采纳
意见反馈 帮助中心 APP下载
官方微信