面试官:线程池是如何做到线程复用的?有了解过吗?

网站建设3年前发布
23 0 0

我们今天探讨ThreadPoolExecutor,一起来看下吧!,我们知道,一个线程在创建的时候会指定一个线程任务,当执行完这个线程任务之后,线程自动销毁。但是线程池却可以复用线程,一个线程执行完线程任务后不销毁,继续执行另外的线程任务。那么它是如何做到的?这得从addWorker()说起。,​retry:可能有些同学没用过,它只是一个标记,它的下一个标记就是for循环,在for循环里面调用continue/break再紧接着retry标记时,就表示从这个地方开始执行continue/break操作,但这不是我们关注的重点。,从上面的代码,我们可以看出,ThreadPoolExecutor在创建线程时,会将线程封装成「工作线程worker」,并放入「工作线程组」中,然后这个worker反复从阻塞队列中拿任务去执行。这个addWorker是excute方法中调用的。,再看 addWorkerFailed(),与上边相反,相当于一个回滚操作,会移除失败的工作线程。,我们接着看Worker对象。,Worker类实现了Runnable接口,所以Worker也是一个线程任务。在构造方法中,创建了一个线程,回过头想想addWorker()里为啥可以t.start()应该很清楚了吧, 并且在构造方法中调用了线程工厂创建了一个线程实例,我们上节讲过线程工厂。其实这也不是关注的重点,重点是这个runWorker()。,​首先去执行创建这个worker时就有的任务,当执行完这个任务后,worker的生命周期并没有结束,在while循环中,worker会不断地调用getTask方法从「阻塞队列」中获取任务然后调用task.run()执行任务,从而达到「复用线程」的目的。只要getTask方法不返回null,此线程就不会退出。,我们接着看getTask()​。,大家有没有想过这里为啥要用take和poll,它们都是出队的操作,这么做有什么好处?,​我们说take()方法会将核心线程阻塞挂起,这样一来它就不会占用太多的cpu资源,直到拿到Runnable 然后返回。,如果「allowCoreThreadTimeOut」设置为true,那么核心线程就会去调用poll方法,因为poll可能会返回null,所以这时候核心线程满足超时条件也会被销毁。,​非核心线程会workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS),如果超时还没有拿到,下一次循环判断「compareAndDecrementWorkerCount」就会返回null,Worker对象的run()方法循环体的判断为null,任务结束,然后线程被系统回收 。,再回头看一下runWorker()是不是设计的很巧妙。,本节内容不是很好理解,想继续探讨的同学可以继续阅读它的源码,这部分内容了解一下就好,其实我们从源码中可以看到大量的线程状态检查,代码写的很健壮,可以从中学习一下。

© 版权声明

相关文章