线程基础:线程池——基本使用(下)

来源:互联网 发布:淘宝摇一摇在哪 编辑:程序博客网 时间:2024/06/07 05:17

5、扩展ThreadPoolExecutor线程池

实际上JAVA中提供的ThreadPoolExecutor线程池是鼓励各位程序员进行扩展的(虽然大多数情况下您无需扩展),并且JBOSS(Netty)、Apache(Camel)也正是这样在做。下面我们看看一些由ThreadPoolExecutor提供的扩展方式。

5-1、Hook methods

在ThreadPoolExecutor中提供了三个专门供子类覆盖/重写的方法:beforeExecute(Thread t, Runnable r)、afterExecute(Runnable r, Throwable t)和terminated()。这三个方法可以帮助程序员,在线程池处理任务的不同阶段,进行额外的业务处理操作:

  • beforeExecute:当线程池正要开始执行某个任务的时候(注意不是任务进入等待队列的时候,是将要开始正式在线程池中执行的时候),线程池会触发这个方法的调用。

  • afterExecute:当线程池完成了某一个任务的执行后,线程池就会触发这个方法。

  • terminated:当线程池本身停止执行的时候,该方法就会被调用。

下面我们给出一段测试代码,用来为各位读者演示以上几个扩展方法的使用效果:


<code class="hljs java has-numbering"><span class="hljs-keyword">package</span> test.thread.pool;<span class="hljs-keyword">import</span> java.util.concurrent.ArrayBlockingQueue;<span class="hljs-keyword">import</span> java.util.concurrent.BlockingQueue;<span class="hljs-keyword">import</span> java.util.concurrent.ThreadPoolExecutor;<span class="hljs-keyword">import</span> java.util.concurrent.TimeUnit;<span class="hljs-keyword">import</span> org.apache.commons.logging.Log;<span class="hljs-keyword">import</span> org.apache.commons.logging.LogFactory;<span class="hljs-keyword">import</span> org.apache.log4j.BasicConfigurator;<span class="hljs-javadoc">/** * 一个扩展的线程池,用于测试ThreadPoolExecutor中的扩展方法 *<span class="hljs-javadoctag"> @author</span> yinwenjie * */</span><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExtendsPool</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ThreadPoolExecutor</span> {</span>    <span class="hljs-keyword">static</span> {        BasicConfigurator.configure();    }    <span class="hljs-javadoc">/**     * 日志     */</span>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Log LOGGER = LogFactory.getLog(ExtendsPool.class);    <span class="hljs-keyword">public</span> <span class="hljs-title">ExtendsPool</span>(<span class="hljs-keyword">int</span> corePoolSize, <span class="hljs-keyword">int</span> maximumPoolSize, <span class="hljs-keyword">long</span> keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {        <span class="hljs-keyword">super</span>(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);    }    <span class="hljs-comment">/* (non-Javadoc)     * @see java.util.concurrent.ThreadPoolExecutor#beforeExecute(java.lang.Thread, java.lang.Runnable)     */</span>    <span class="hljs-annotation">@Override</span>    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">beforeExecute</span>(Thread t, Runnable r) {        TestRunnable testRunnable = (TestRunnable)r;        ExtendsPool.LOGGER.info(<span class="hljs-string">"beforeExecute(Thread t, Runnable r) : "</span> + testRunnable.getIndex());    }    <span class="hljs-comment">/* (non-Javadoc)     * @see java.util.concurrent.ThreadPoolExecutor#afterExecute(java.lang.Runnable, java.lang.Throwable)     */</span>    <span class="hljs-annotation">@Override</span>    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">afterExecute</span>(Runnable r, Throwable t) {        TestRunnable testRunnable = (TestRunnable)r;        ExtendsPool.LOGGER.info(<span class="hljs-string">"afterExecute(Runnable r, Throwable t) : "</span> + testRunnable.getIndex());    }    <span class="hljs-comment">/* (non-Javadoc)     * @see java.util.concurrent.ThreadPoolExecutor#terminated()     */</span>    <span class="hljs-annotation">@Override</span>    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">terminated</span>() {        ExtendsPool.LOGGER.info(<span class="hljs-string">"terminated() !!"</span>);    }    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) <span class="hljs-keyword">throws</span> Throwable {        <span class="hljs-comment">// 这个做法,是故意让后续index == 5 - 9的线程进入等待队列。以便观察执行现象</span>        ExtendsPool extendsPool = <span class="hljs-keyword">new</span> ExtendsPool(<span class="hljs-number">5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">10000</span>, TimeUnit.MILLISECONDS, <span class="hljs-keyword">new</span> ArrayBlockingQueue<Runnable>(<span class="hljs-number">5</span>));        <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> index = <span class="hljs-number">0</span> ; index < <span class="hljs-number">10</span> ; index ++) {            <span class="hljs-comment">// 一定要使用execute,不要使用submit。后文讲解原因</span>            extendsPool.execute(<span class="hljs-keyword">new</span> TestRunnable(index));        }        <span class="hljs-comment">// 发出停止指令。注意停止指令本身不会等待,要使用awaitTermination进行等待。</span>        <span class="hljs-comment">// 注意,按照我们上文讲过的线程池的工作原理,线程池在收到shutdown终止指令后</span>        <span class="hljs-comment">// 就不会再接受提交过来的任务了,无论“核心线程”、等待队列处于什么样的状态!</span>        extendsPool.shutdown();        <span class="hljs-comment">// 当所有任务执行完成后,终止线程池的运行</span>        extendsPool.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES);    }    <span class="hljs-javadoc">/**     * 这个就是测试用的线程     *<span class="hljs-javadoctag"> @author</span> yinwenjie     */</span>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestRunnable</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Runnable</span> {</span>        <span class="hljs-javadoc">/**         * 记录任务的唯一编号,这样在日志中好做识别         */</span>        <span class="hljs-keyword">private</span> Integer index;        <span class="hljs-keyword">public</span> <span class="hljs-title">TestRunnable</span>(<span class="hljs-keyword">int</span> index) {            <span class="hljs-keyword">this</span>.index = index;        }        <span class="hljs-javadoc">/**         *<span class="hljs-javadoctag"> @return</span> the index         */</span>        <span class="hljs-keyword">public</span> Integer <span class="hljs-title">getIndex</span>() {            <span class="hljs-keyword">return</span> index;        }        <span class="hljs-annotation">@Override</span>        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>() {            <span class="hljs-comment">/*             * 线程中,就只做一件事情:             * 等待10秒钟的事件,以便模拟业务操作过程             * */</span>            Thread currentThread  = Thread.currentThread();            <span class="hljs-keyword">synchronized</span> (currentThread) {                <span class="hljs-keyword">try</span> {                    currentThread.wait(<span class="hljs-number">10000</span>);                } <span class="hljs-keyword">catch</span> (InterruptedException e) {                    e.printStackTrace(System.out);                }            }        }    }}</code>

以上代码中,我们一共有10个任务,编号分别任0-9。我们使用的线程池只会有5个核心线程,这样是为了让后续index为5-9的任务先进入ArrayBlockingQueue等待队列。我们的意图是将者10个任务分成两批送入线程池中进行运行,并且在10个任务都执行结束后,终止线程池的运行。

以下为上面示例代码的执行效果:

<code class="hljs avrasm has-numbering"><span class="hljs-number">0</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">2</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - beforeExecute(Thread t, Runnable r) : <span class="hljs-number">1</span><span class="hljs-number">2</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">4</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - beforeExecute(Thread t, Runnable r) : <span class="hljs-number">3</span><span class="hljs-number">1</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">5</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - beforeExecute(Thread t, Runnable r) : <span class="hljs-number">4</span><span class="hljs-number">0</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">1</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - beforeExecute(Thread t, Runnable r) : <span class="hljs-number">0</span><span class="hljs-number">0</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">3</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - beforeExecute(Thread t, Runnable r) : <span class="hljs-number">2</span><span class="hljs-number">10004</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">2</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - afterExecute(Runnable r, Throwable t) : <span class="hljs-number">1</span><span class="hljs-number">10004</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">4</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - afterExecute(Runnable r, Throwable t) : <span class="hljs-number">3</span><span class="hljs-number">10005</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">2</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - beforeExecute(Thread t, Runnable r) : <span class="hljs-number">5</span><span class="hljs-number">10005</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">4</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - beforeExecute(Thread t, Runnable r) : <span class="hljs-number">6</span><span class="hljs-number">10008</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">5</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - afterExecute(Runnable r, Throwable t) : <span class="hljs-number">4</span><span class="hljs-number">10009</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">3</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - afterExecute(Runnable r, Throwable t) : <span class="hljs-number">2</span><span class="hljs-number">10008</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">1</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - afterExecute(Runnable r, Throwable t) : <span class="hljs-number">0</span><span class="hljs-number">10010</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">3</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - beforeExecute(Thread t, Runnable r) : <span class="hljs-number">8</span><span class="hljs-number">10009</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">5</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - beforeExecute(Thread t, Runnable r) : <span class="hljs-number">7</span><span class="hljs-number">10010</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">1</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - beforeExecute(Thread t, Runnable r) : <span class="hljs-number">9</span><span class="hljs-number">20006</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">2</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - afterExecute(Runnable r, Throwable t) : <span class="hljs-number">5</span><span class="hljs-number">20009</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">4</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - afterExecute(Runnable r, Throwable t) : <span class="hljs-number">6</span><span class="hljs-number">20011</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">3</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - afterExecute(Runnable r, Throwable t) : <span class="hljs-number">8</span><span class="hljs-number">20012</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">1</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - afterExecute(Runnable r, Throwable t) : <span class="hljs-number">9</span><span class="hljs-number">20012</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">5</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - afterExecute(Runnable r, Throwable t) : <span class="hljs-number">7</span><span class="hljs-number">20013</span> [pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">5</span>] INFO test<span class="hljs-preprocessor">.thread</span><span class="hljs-preprocessor">.pool</span><span class="hljs-preprocessor">.ExtendsPool</span>  - terminated() !!</code>

5-2、execute方法和submit方法的区别

上面的示例代码中,我们使用的是execute方法来提交任务;而上文有的示例代码中,我们使用的是submit方法提交任务。ThreadPoolExecutor线程池中,这两种方法提交任务都是可以的,但是他们的工作原理是不一样的:

  • execute方法:所有实现了Runnable接口的任务都可以使用execute方法进行提交。而实现了Runnable接口的任务,并没有提供任何“标准”的方式为我们返回任务的执行结果(这是我们还没有讲到的知识点)。线程在线程池中运行结束了,就结束了。所以,使用execute方法提交的任务,程序员并不能在任务执行完成后,获得一个“标准”的执行结果

  • submit方法:submit方法提交的任务是实现了Callable接口的任务(这是我们还没有讲到的知识点)。Callable接口的特性是,在其运行完成后,会返回一个“标准”的执行结果。

但有的读者可能会问,不是说submit方法也可以提交实现Runnable接口的任务吗?你之前也是这么使用的。是的,submit方法也可以提交实现Runnable接口的任务,但是处理方式和execute方法的处理方式完全不同:使用submit方法提交的实现了Runnable接口的任务,将会被封装到 线程池内部使用Executors工具中callable方法创建的RunnableAdapter对象中。源代码片段如下:

<code class="hljs axapta has-numbering"><span class="hljs-comment">// JAVA提供的Executors 工具类</span><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Executors</span> {</span>    。。。。。。    <span class="hljs-comment">// 这个工具方法可以将一个Runnable任务转换为一个Callable任务</span>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <T> Callable<T> callable(Runnable task, T result) {        <span class="hljs-keyword">if</span> (task == <span class="hljs-keyword">null</span>)            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NullPointerException();        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RunnableAdapter<T>(task, result);    }    <span class="hljs-comment">// 实际上您提交的任务,作为了这个类的一个属性。</span>    <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RunnableAdapter</span><<span class="hljs-title">T</span>> <span class="hljs-inheritance"><span class="hljs-keyword">implements</span></span> <span class="hljs-title">Callable</span><<span class="hljs-title">T</span>> {</span>        <span class="hljs-keyword">final</span> Runnable task;        <span class="hljs-keyword">final</span> T result;        RunnableAdapter(Runnable task, T result) {            <span class="hljs-keyword">this</span>.task = task;            <span class="hljs-keyword">this</span>.result = result;        }        <span class="hljs-keyword">public</span> T call() {            task.run();            <span class="hljs-keyword">return</span> result;        }    }    。。。。。。}</code>

所以为什么在5-1小结第一段示例代码中,我会说明只能使用extendsPool.execute提交任务, 而不要使用extendsPool.submit提交任务。因为如果使用extendsPool.submit提交任务,那么您使用的hook method:beforeExecute和afterExecute,虽然可以拿到一个Runnable对象,但是这个Runnable对象却不是您创建的Runnable任务本身。而是一个FutureTask对象,里面封装了一个RunnableAdapter对象,在RunnableAdapter对象里面,才是您的Runnable任务本身:

这里写图片描述

如果您非要使用submit提交任务,那么在执行到:TestRunnable testRunnable = (TestRunnable)r; 的时候,就会抛出对象类型转换错误(不过您理解了原因后,当然可以改示例代码了)。我们在后续的文章中,将专门花一定的篇幅介绍Callable接口。

6、工具类

6-1、Executors工具类

Executors是一个用于创建各种线程池特性的工具类。通常情况下,您使用这个工具类创建的线程池就可以涵盖90%以上的业务场景了。

如果您观察一下Executors类的源代码,您就可以发现Executors工具类实际上就是帮助您完成了特定线程池的创建过程。

  • 创建一个固定大小的ThreadPoolExecutor线程池:
<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ExecutorService <span class="hljs-title">newFixedThreadPool</span>(<span class="hljs-keyword">int</span> nThreads) {    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ThreadPoolExecutor(nThreads, nThreads,                                  <span class="hljs-number">0</span>L, TimeUnit.MILLISECONDS,                                  <span class="hljs-keyword">new</span> LinkedBlockingQueue<Runnable>());}</code>

以上代码片段就是Executors工具类创建固定大小的ThreadPoolExecutor线程池的过程。实际上就是把corePoolSize 和maximumPoolSize 的值设置成一样,也就是线程池中只有“核心线程”——那些创建了就不会回收的线程。并且线程池的等待队列设置为LinkedBlockingQueue——一种没有数量限制的先进先出队列。

  • 创建一个只有一个线程的ThreadPoolExecutor线程池:
<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ExecutorService <span class="hljs-title">newSingleThreadExecutor</span>() {  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> FinalizableDelegatedExecutorService        (<span class="hljs-keyword">new</span> ThreadPoolExecutor(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>,                                <span class="hljs-number">0</span>L, TimeUnit.MILLISECONDS,                                <span class="hljs-keyword">new</span> LinkedBlockingQueue<Runnable>()));}</code>

这段代码在Executors工具类中,用于创建只有一个固定线程的线程池。即corePoolSize 和maximumPoolSize的值都等于1。这根线程也不会被回收,然后将LinkedBlockingQueue这个无大小限制的先进先出队列中的每个任务,按照顺序进行处理。

显然,处理的即时性并不是这中特性的线程池关心的重点;它的重点显然是保证LinkedBlockingQueue中的等待任务能够按照既定顺序被处理

  • 创建一个“Cache”线程池:
<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ExecutorService <span class="hljs-title">newCachedThreadPool</span>() {  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ThreadPoolExecutor(<span class="hljs-number">0</span>, Integer.MAX_VALUE,                                  <span class="hljs-number">60</span>L, TimeUnit.SECONDS,                                  <span class="hljs-keyword">new</span> SynchronousQueue<Runnable>());}</code>

谁说corePoolSize的大小不能为0?!,当只有maximumPoolSize参数有值的时候,代表无论什么时候,线程池中等待超时的线程都将会被回收。注意,CachedThreadPool所设置的等待队列的类型是SynchronousQueue,意味着任务将会立即被送入线程池执行(如果这时没有空闲的线程,则立即创建新的线程)。

  • 将一个Runnable接口转换成Callable接口:
<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <T> Callable<T> <span class="hljs-title">callable</span>(Runnable task, T result) {    <span class="hljs-keyword">if</span> (task == <span class="hljs-keyword">null</span>)        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NullPointerException();    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RunnableAdapter<T>(task, result);}</code>

这又是Executors提供的一个好用的工具方法,将没有执行值返回的Runnable接口的实现,转换为一个有执行返回值的Callable接口。就像上文提到的一样,ThreadPoolExecutor类中的submit(Runnable task)方法,就是使用这种方式实现了Runnable接口 到 Callable接口的转变的。

6-2、对象池:Apache中的扩展?

在百度或者Google上,您可以搜索:“common-pool2 线程池”,并且找到很多搜索结果。本来在写作之初,我想在介绍JAVA原生线程池的时候,顺便介绍Apache-common-pool2中提供的对象池的使用。但是后来想想其实这两者并没有什么直接连接,而且两者的差异实际上比较大

线程池中运行的就是线程,如果你使用JConsole或者jvisualvm等监控工具,通过JMX协议你可以看到线程池中运行的每一个线程,并作dump操作;对象池是对一组实例对象进行管理的集合,对象池不会因为要生成、发送、回收某一个对象,就为某一次执行创建一个线程。如果硬是要将对象池和线程池做硬性的关联,那就只有一种情况:“拥有线程对象的对象池”。这样的问题是,对象池并没有提供像线程池那样针对线程管理所需要的特定方法

例如,拥有线程对象的对象池并没有“等待队列”的特性;再例如,拥有线程对象的对象池,并没有义务启动线程、回收多余线程。也就是说它没有管理线程执行过程的义务,只负责创建线程对象、回收线程对象;所有使用对象池管理线程对象的做法是不科学的

6-3、ThreadPoolExecutor中常用属性含义

这一小节,我们看着ThreadPoolExecutor的源代码,再来总结一下ThreadPoolExecutor线程池中我们已经介绍过和没有介绍过的属性。当然在实际工作中,您不需要随时随地的记住这些属性的含义,但是理解这些属性的意义,至少可以在同事面前炫一下,哈哈:

<code class="hljs java has-numbering"><span class="hljs-javadoc">/** * 等待队列,这几篇文章我们已经花了很大的篇幅来介绍线程池的等待队列 */</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> BlockingQueue<Runnable> workQueue;<span class="hljs-javadoc">/** * ReentrantLock是JDK 1.5中引入的一个锁机制,后续的文章我会专门讲解ReentrantLock锁。 * 线程池主要在创建线程、回收线程、终止等操作的时候,使用ReentrantLock,保证线程池对象中的状态一致性 */</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> ReentrantLock mainLock = <span class="hljs-keyword">new</span> ReentrantLock();<span class="hljs-javadoc">/** * 线程池中一个任务就是一个worker,这个集合用于存储正在线程池中运行的任务 */</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> HashSet<Worker> workers = <span class="hljs-keyword">new</span> HashSet<Worker>();<span class="hljs-javadoc">/** * 这个参数记录,线程池曾经达到过的最大的“池大小” */</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> largestPoolSize;<span class="hljs-javadoc">/** * 这个属性记录线程池已经完成的任务数量 */</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">long</span> completedTaskCount;<span class="hljs-comment">/* * All user control parameters are declared as volatiles so that * ongoing actions are based on freshest values, but without need * for locking, since no internal invariants depend on them * changing synchronously with respect to other actions. */</span><span class="hljs-javadoc">/** * 创建线程使用的线程工程 */</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> ThreadFactory threadFactory;<span class="hljs-javadoc">/** * 指定的“拒绝处理器”,系统中还有一个默认的“拒绝处理器”:defaultHandler =new AbortPolicy(); */</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> RejectedExecutionHandler handler;<span class="hljs-javadoc">/** * 空闲线程在等待工作时间超时。 */</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">long</span> keepAliveTime;<span class="hljs-javadoc">/** * 是否在超过等待时间后,就连线程池中小于corePoolSize的“核心线程”对象也进行回收。 * 默认情况为false */</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">boolean</span> allowCoreThreadTimeOut;<span class="hljs-javadoc">/** * “核心线程”的大小。小于或者等于这个数量的线程,即便超过keepAliveTime也不会被回收, * 除非allowCoreThreadTimeOut设置为true */</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">int</span> corePoolSize;<span class="hljs-javadoc">/** * 线程池中最大的线程数量 */</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">int</span> maximumPoolSize;<span class="hljs-javadoc">/** * 默认的“拒绝处理器” */</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> RejectedExecutionHandler defaultHandler =<span class="hljs-keyword">new</span> AbortPolicy();</code>

6-4、与spring结合

当然JAVA的ThreadPoolExecutor线程池还可以和Spring框架集成在一起。如果您理解Spring的容器原理,您就知道不能使用new关键字创建ThreadPoolExecutor线程池了。如果您不具备Spring的知识,请参考相关学习资料。

下面我为读者提供一段我自己在工作中使用的spring配置,将ThreadPoolExecutor线程池注入到Spring容器中。这样做的目的,是为了将某个线程中处理的任务置于Spring容器的控制下。


0 0
原创粉丝点击