随着CPU核心数量的增加,必然要采用多线程技术来充分发挥其计算能力。因此,多线程技术是服务器开发人员必须掌握的技术。线程的创建和销毁都涉及到系统调用,会消耗系统资源,所以引入线程池技术来避免线程的频繁创建和销毁。Java中有一个Executors工具类可以为我们创建线程池,其实质就是创建一个新的ThreadPoolExecutor对象。线程池几乎是面试的必考题。本节结合源码说说ThreadExecutor的工作原理1.线程池的创建先来看ThreadPoolExecutor参数最全的构造方法:图片corePoolSize:线程池的核心线程数,说白了,即使线程池中没有任务,也会有corePoolSize个线程在等待任务。maximumPoolSize:最大线程数,无论你提交多少任务,线程池中工作线程的最大数量为maximumPoolSize。keepAliveTime:线程的存活时间。当线程池中的线程数大于corePoolSize时,如果等待keepAliveTime后没有任务执行,线程就会退出。unit:这个用来指定keepAliveTime的单位,比如秒:TimeUnit.SECONDS。workQueue:一个阻塞队列,提交的任务会放在这个队列中。threadFactory:线程工厂,用于创建线程,主要是给线程命名,默认工厂线程名:pool-1-thread-3。handler:拒绝策略,当线程池中线程耗尽,队列满时调用。以上是创建线程池时用到的参数。面试官在面试的时候经常会问这个问题。2.线程池执行过程下面用一张图来说明线程池的执行过程。图片任务提交到线程池。它会先判断当前线程数是否小于corePoolSize。如果小于corePoolSize,将创建一个线程来执行提交的任务,否则任务将被释放。进入workQueue队列,如果workQueue已满,判断当前线程数是否小于maximumPoolSize,小于则创建线程执行任务,否则调用handler表示线程池拒绝接收任务。这里以jdk1.8.0_111的源码为例,看看具体实现。1.首先看一下线程池的executor方法的图片。判断当前活跃线程数是否小于corePoolSize。如果小于则调用addWorker创建线程执行任务。如果不小于corePoolSize,则将任务添加到workQueue队列中。如果放入workQueue失败,则创建线程执行任务。如果此时创建线程失败(当当前线程数不小于maximumPoolSize时),就会调用reject(内部调用处理程序)拒绝接受任务。2.看addWorker方法在创建非核心线程时实现这段代码,即core等于false。判断当前线程数是否大于等于maximumPoolSize,大于等于则返回false,即上述③中创建线程失败。addWorker方法的后半部分:Image创建一个Worker对象,同时实例化一个Thread对象。启动这个线程3.去Worker里面看它的实现图,可以看到在创建Worker的时候会调用threadFactory来创建线程。上面②中启动一个线程,会触发线程调用worker的run方法。4、接下来我们看一下runWorker方法的逻辑图。线程调用runWoker,会在while循环中调用getTask方法从workerQueue中读取任务,然后执行任务。只要getTask方法不返回null,这个线程就不会退出。5、最后我们来看一下getTask方法的图片。让我们忽略allowCoreThreadTimeOut。此变量的默认值为false。wc>corePoolSize是判断当前线程数是否大于corePoolSize。如果当前线程数大于corePoolSize,会调用workQueue的poll方法获取任务,超时时间为keepAliveTime。如果超过keepAliveTime的长度,poll返回null,上面提到的while序列退出,线程执行完毕。如果当前线程数小于corePoolSize,则调用workQueue的take方法在当前时刻阻塞。
