线程池

<div id="article_content" class="article_content clearfix">
        <link rel="stylesheet" href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/ck_htmledit_views-b5506197d8.css">
                <div id="content_views" class="markdown_views prism-atom-one-dark">
                    <svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
                        <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
                    </svg>
                    <h4><a id="1_Java_0"></a>1. Java的线程池</h4> 
<h5><a id="__1"></a>① 合理使用线程池的好处</h5> 
<ul><li><strong>Java的线程池</strong>是<strong>运用场景最多的并发框架</strong>,<strong>几乎所有需要异步或者并发执行任务的程序</strong>都可以<strong>使用线程池</strong>。</li><li>合理使用线程池能带来的好处:</li></ul> 
<ol><li><strong>降低资源消耗。</strong> 通过<code>重复利用已经创建的线程</code><strong>降低</strong><code>线程创建的和销毁</code>造成的消耗。例如,工作线程Woker会无线循环获取阻塞队列中的任务来执行。</li><li><strong>提高响应速度。</strong> 当任务到达时,<code>任务可以不需要等到线程创建就能立即执行</code>。</li><li><strong>提高线程的可管理性。</strong> 线程是稀缺资源,Java的线程池可以对线程资源进行<code>统一分配</code>、<code>调优</code>和<code>监控</code>。</li></ol> 
<h5><a id="__8"></a>② 线程池的工作流程</h5> 
<ul><li>一个新的任务到线程池时,线程池的处理流程如下:<br> 1.<code>线程池判断核心线程池里的线程是否都在执行任务。</code> 如果不是,<code>创建一个新的工作线程来执行任务</code>。如果核心线程池里的线程都在执行任务,则进入下个流程。</li></ul> 
<ol start="2"><li><code>线程池判断阻塞队列是否已满。</code> 如果阻塞队列没有满,<code>则将新提交的任务存储在阻塞队列中</code>。如果阻塞队列已满,则进入下个流程。</li><li><code>线程池判断线程池里的线程是否都处于工作状态。</code> 如果没有,则创建一个新的工作线程来执行任务。如果已满,则交给饱和策略来处理这个任务。<br> <img src="https://img-blog.csdnimg.cn/20190722201421710.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0NTQ1Mzg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></li></ol> 
<ul><li>线程池的核心实现类是<code>ThreadPoolExecutor类</code>,用来执行提交的任务。因此,任务提交到线程池时,具体的处理流程是由<code>ThreadPoolExecutor类</code>的<strong>execute()方法</strong>去完成的。<br> <img src="https://img-blog.csdnimg.cn/20190722202417970.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0NTQ1Mzg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></li></ul> 
<ol><li>如果当前运行的线程少于<code>corePoolSize</code>,则创建新的工作线程来执行任务(<strong>执行这一步骤需要获取全局锁</strong>)。</li><li>如果当前运行的线程大于或等于<code>corePoolSize</code>,而且<code>BlockingQueue</code>未满,则将任务加入到<code>BlockingQueue</code>中。</li><li>如果<code>BlockingQueue</code>已满,而且当前运行的线程小于<code>maximumPoolSize</code>,则创建新的工作线程来执行任务(<strong>执行这一步骤需要获取全局锁</strong>)。</li><li>如果当前运行的线程大于或等于<code>maximumPoolSize</code>,<code>任务将被拒绝</code>,并调用<code>RejectExecutionHandler.rejectExecution()方法</code>。即<strong>调用饱和策略对任务进行处理。</strong></li></ol> 
<ul><li><strong>工作线程(Worker):</strong> 线程池在创建线程时,会将线程封装成<strong>工作线程Woker</strong>。Woker在执行完任务后,不是立即销毁而是<code>循环获取阻塞队列里的任务来执行</code>。</li></ul> 
<h5><a id="_7_22"></a>③ 线程池的创建(7个参数)</h5> 
<ul><li>可以通过<code>ThreadPoolExecutor</code>来创建一个线程池:</li></ul> 
<pre class="prettyprint"><code class="prism language-java has-numbering" οnclick="mdcp.copyCode(event)" style="position: unset;"><span class="token keyword">new</span> <span class="token class-name">ThreadPoolExecutor</span><span class="token punctuation">(</span><span class="token keyword">int</span> corePoolSize<span class="token punctuation">,</span> <span class="token keyword">int</span> maximumPoolSize<span class="token punctuation">,</span> <span class="token keyword">long</span> keepAliveTime<span class="token punctuation">,</span> 
    TimeUnit unit<span class="token punctuation">,</span> BlockingQueue<span class="token generics function"><span class="token punctuation">&lt;</span>Runnable<span class="token punctuation">&gt;</span></span> workQueue<span class="token punctuation">,</span> RejectedExecutionHandler handler<span class="token punctuation">)</span>
<div class="hljs-button {2}" data-title="复制" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.4259&quot;}"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li></ul></pre> 
<ul><li><strong>corePoolSize(线程池的基本大小):</strong></li></ul> 
<ol><li>提交一个任务到线程池时,线程池会创建一个新的线程来执行任务。<strong>注意:</strong> 即使有<code>空闲的基本线程</code>能执行该任务,也会创建新的线程。</li><li>如果线程池中的线程数已经大于或等于<code>corePoolSize</code>,则不会创建新的线程。</li><li>如果调用了线程池的<code>prestartAllCoreThreads()方法</code>,线程池会<code>提前创建并启动所有基本线程</code>。</li></ol> 
<ul><li><strong>maximumPoolSize(线程池的最大数量):</strong> 线程池允许创建的最大线程数。</li></ul> 
<ol><li>阻塞队列已满,线程数小于<code>maximumPoolSize</code>便可以创建新的线程执行任务。</li><li>如果<strong>使用无界的阻塞队列</strong>,<code>该参数没有什么效果</code>。</li></ol> 
<ul><li><strong>workQueue(工作队列):</strong> 用于保存等待执行的任务的阻塞队列。</li></ul> 
<ol><li><code>ArrayBlockingQueue:</code> 基于数组结构的<strong>有界阻塞队列</strong>,按<strong>FIFO(先进先出)原则</strong>对任务进行排序。使用该队列,线程池中能创建的最大线程数为<code>maximumPoolSize</code>。</li><li><code>LinkedBlockingQueue:</code> 基于链表结构的<strong>无界阻塞队列</strong>,按<strong>FIFO(先进先出)原则</strong>对任务进行排序,吞吐量高于<code>ArrayBlockingQueue</code>。使用该队列,线程池中能创建的最大线程数为<code>corePoolSize</code>。<strong>静态工厂方法</strong> <code>Executor.newFixedThreadPool()</code>使用了这个队列。</li><li><code>SynchronousQueue:</code> 一个<strong>不存储元素</strong>的阻塞队列。<code>添加任务的操作必须等到另一个线程的移除操作</code>,<code>否则添加操作一直处于阻塞状态</code>。<strong>静态工厂方法</strong> <code>Executor.newCachedThreadPool()</code>使用了这个队列。</li><li><code>PriorityBlokingQueue:</code> 一个<strong>支持优先级</strong>的<strong>无界阻塞队列</strong>。使用该队列,线程池中能创建的最大线程数为<code>corePoolSize</code>。</li></ol> 
<ul><li><strong>keepAliveTime(线程活动保持时间):</strong> 线程池的<code>工作线程空闲后</code>,<code>保持存活的时间</code>。如果<code>任务多而且任务的执行时间比较短</code>,可以<strong>调大</strong><code>keepAliveTime</code>,提高线程的利用率。</li><li><strong>unit(线程活动保持时间的单位):</strong> 可选单位有<code>DAYS</code>、<code>HOURS</code>、<code>MINUTES</code>、<code>毫秒</code>、<code>微秒</code>、纳<code>秒</code>。</li><li><strong>handler(饱和策略,或者又称拒绝策略):</strong> 当<strong>队列和线程池都满了</strong>,即<strong>线程池饱和了</strong>,必须采取一种策略处理提交的新任务。</li></ul> 
<ol><li><code>AbortPolicy:</code> 无法处理新任务时,<code>直接抛出异常</code>,这是<strong>默认策略</strong>。</li><li><code>CallerRunsPolicy:</code>用调用者所在的线程来执行任务。</li><li><code>DiscardOldestPolicy:</code>丢弃阻塞队列中<strong>最靠前</strong>的一个任务,并执行当前任务。</li><li><code>DiscardPolicy:</code> 直接丢弃任务。</li></ol> 
<ul><li><strong>threadFactory:</strong> 构建线程的工厂类</li><li><strong>总结:</strong></li></ul> 
<ol><li>常用的5个,核心池、最大池、空闲时间、时间的单位、阻塞队列;另外两个:拒绝策略、线程工厂类</li><li>常见线程池的创建参数如下。<strong>PS:</strong> <code>CachedThreadPool</code>核心池为0,最大池为<code>Integer.MAX_VALUE</code>,相当于<strong>只使用了最大池</strong>;其他线程池,核心池与最大池一样大,因此相当于<strong>只用了核心池</strong>。</li></ol> 
<pre class="prettyprint"><code class="prism language-java has-numbering" οnclick="mdcp.copyCode(event)" style="position: unset;">FixedThredPool<span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">ThreadExcutor</span><span class="token punctuation">(</span>n<span class="token punctuation">,</span> n<span class="token punctuation">,</span> <span class="token number">0</span>L<span class="token punctuation">,</span> ms<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">LinkedBlockingQueue</span><span class="token generics function"><span class="token punctuation">&lt;</span>Runable<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span>
SingleThreadExecutor<span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">ThreadExcutor</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span>L<span class="token punctuation">,</span> ms<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">LinkedBlockingQueue</span><span class="token generics function"><span class="token punctuation">&lt;</span>Runable<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
CachedTheadPool<span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">ThreadExcutor</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> max_valuem<span class="token punctuation">,</span> <span class="token number">60</span>L<span class="token punctuation">,</span> s<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">SynchronousQueue</span><span class="token generics function"><span class="token punctuation">&lt;</span>Runnable<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
ScheduledThreadPoolExcutor<span class="token operator">:</span> ScheduledThreadPool<span class="token punctuation">,</span> SingleThreadScheduledExecutor<span class="token punctuation">.</span>
<div class="hljs-button {2}" data-title="复制" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.4259&quot;}"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li></ul></pre> 
<ol start="3"><li>如果使用的阻塞队列为<strong>无界队列</strong>,则<strong>永远不会调用拒绝策略</strong>,因为再多的任务都可以放在队列中。</li><li><code>SynchronousQueue</code>是<strong>不存储任务</strong>的,新的任务要么立即被已有线程执行,要么创建新的线程执行。</li></ol> 
<h5><a id="__61"></a>④ 向线程池提交任务</h5> 
<ul><li>使用<code>ThreadPoolEXecutor.executor()</code>方法来提交任务:</li></ul> 
<pre class="prettyprint"><code class="prism language-java has-numbering" οnclick="mdcp.copyCode(event)" style="position: unset;"><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">execute</span><span class="token punctuation">(</span>Runnable command<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    <span class="token comment">// command为null,抛出NullPointerException</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>command <span class="token operator">==</span> null<span class="token punctuation">)</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">NullPointerException</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>      
    <span class="token keyword">int</span> c <span class="token operator">=</span> ctl<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 线程池中的线程数小于corePoolSize,创建新的线程</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">workerCountOf</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token operator">&lt;</span> corePoolSize<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">addWorker</span><span class="token punctuation">(</span>command<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment">// 创建工作线程</span>
            <span class="token keyword">return</span><span class="token punctuation">;</span>
        c <span class="token operator">=</span> ctl<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token comment">// 将任务添加到阻塞队列,如果</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isRunning</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> workQueue<span class="token punctuation">.</span><span class="token function">offer</span><span class="token punctuation">(</span>command<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token keyword">int</span> recheck <span class="token operator">=</span> ctl<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span> <span class="token function">isRunning</span><span class="token punctuation">(</span>recheck<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token function">remove</span><span class="token punctuation">(</span>command<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token function">reject</span><span class="token punctuation">(</span>command<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">workerCountOf</span><span class="token punctuation">(</span>recheck<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token function">addWorker</span><span class="token punctuation">(</span>null<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token comment">// 阻塞队列已满,尝试创建新的线程,如果超过maximumPoolSize,执行handler.rejectExecution()</span>
    <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">addWorker</span><span class="token punctuation">(</span>command<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token function">reject</span><span class="token punctuation">(</span>command<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<div class="hljs-button {2}" data-title="复制" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.4259&quot;}"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li><li style="color: rgb(153, 153, 153);">2</li><li style="color: rgb(153, 153, 153);">3</li><li style="color: rgb(153, 153, 153);">4</li><li style="color: rgb(153, 153, 153);">5</li><li style="color: rgb(153, 153, 153);">6</li><li style="color: rgb(153, 153, 153);">7</li><li style="color: rgb(153, 153, 153);">8</li><li style="color: rgb(153, 153, 153);">9</li><li style="color: rgb(153, 153, 153);">10</li><li style="color: rgb(153, 153, 153);">11</li><li style="color: rgb(153, 153, 153);">12</li><li style="color: rgb(153, 153, 153);">13</li><li style="color: rgb(153, 153, 153);">14</li><li style="color: rgb(153, 153, 153);">15</li><li style="color: rgb(153, 153, 153);">16</li><li style="color: rgb(153, 153, 153);">17</li><li style="color: rgb(153, 153, 153);">18</li><li style="color: rgb(153, 153, 153);">19</li><li style="color: rgb(153, 153, 153);">20</li><li style="color: rgb(153, 153, 153);">21</li><li style="color: rgb(153, 153, 153);">22</li></ul></pre> 
<h5><a id="__88"></a>⑤ 线程池的五种运行状态</h5> 
<ul><li><strong>RUNNING :</strong> 该状态的线程池<strong>既能接受新提交的任务</strong>,<strong>又能处理阻塞队列中任务</strong>。</li><li><strong>SHUTDOWN:</strong><code>该状态的线程池**不能接收新提交的任务**,**但是能处理阻塞队列中的任务**。</code>(政府服务大厅不在允许群众拿号了,处理完手头的和排队的政务就下班。)`</li></ul> 
<ol><li>处于 <code>RUNNING 状态</code>时,调用 <code>shutdown()方法</code>会使线程池进入到该状态。</li><li><strong>注意:</strong> <code>finalize() 方法</code>在执行过程中也会<strong>隐式调用</strong><code>shutdown()方法</code>。</li></ol> 
<ul><li><strong>STOP:</strong> 该状态的线程池<strong>不接受新提交的任务</strong>,也<strong>不处理在阻塞队列中的任务</strong>,<strong>还会中断正在执行的任务</strong>。<code>(政府服务大厅不再进行服务了,拿号、排队、以及手头工作都停止了。)</code></li></ul> 
<ol><li>在线程池处于 <code>RUNNING 或 SHUTDOWN 状态</code>时,调用 <code>shutdownNow() 方法</code>会使线程池进入到该状态;</li></ol> 
<ul><li><strong>TIDYING:</strong> 如果<code>所有的任务都已终止</code>,<code>workerCount (有效线程数)=0</code> 。</li></ul> 
<ol><li>线程池进入该状态后会调用 <code>terminated() 钩子方法</code>进入<code>TERMINATED 状态</code>。</li></ol> 
<ul><li><strong>TERMINATED:</strong> 在<code>terminated()钩子方法</code>执行完后进入该状态,<strong>默认<code>terminated()钩子方法</code>中什么也没有做</strong>。<br> <img src="https://img-blog.csdnimg.cn/20190722225124481.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0NTQ1Mzg=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></li></ul> 
<h5><a id="_shutdownshutdownNow_99"></a>⑥ 线程池的关闭(<code>shutdown或者shutdownNow方法</code>)</h5> 
<ul><li>可以通过调用线程池的<code>shutdown或者shutdownNow方法</code>来关闭线程池:遍历线程池中工作线程,逐个调用<code>interrupt方法</code>来<strong>中断线程</strong>。</li><li><strong>shutdown方法与shutdownNow的特点:</strong></li></ul> 
<ol><li><code>shutdown方法</code>将线程池的状态设置为<code>SHUTDOWN状态</code>,<strong>只会中断空闲的工作线程</strong>。</li><li><code>shutdownNow方法</code>将线程池的状态设置为<code>STOP状态</code>,<strong>会中断所有工作线程</strong>,不管工作线程是否空闲。</li><li>调用两者中任何一种方法,都会使<code>isShutdown方法</code>的返回值为true;<code>线程池中所有的任务都关闭后</code>,<code>isTerminated方法</code>的返回值为true。</li><li>通常使用<code>shutdown方法</code>关闭线程池,如果不要求任务一定要执行完,则可以调用<code>shutdownNow方法</code>。</li></ol> 
<h4><a id="2_java_107"></a>2. java线程池的调优以及监控</h4> 
<h5><a id="__108"></a>① 线程池的调优(线程池的合理配置)</h5> 
<ul><li>先从以下几个角度分析任务的特性:</li></ul> 
<ol><li><strong>任务的性质:</strong> <code>CPU 密集型任务</code>、<code>IO 密集型任务</code>和<code>混合型任务</code>。</li><li><strong>任务的优先级:</strong> 高、中、低。</li><li><strong>任务的执行时间:</strong> 长、中、短。</li><li><strong>任务的依赖性:</strong> <code>是否依赖其他系统资源</code>,如<code>数据库连接</code>。</li></ol> 
<ul><li><strong>任务性质不同的任务可以用不同规模的线程池分开处理。</strong> 可以通过 <code>Runtime.getRuntime().availableProcessors()</code> 方法获得当前设备的 CPU 个数。</li></ul> 
<ol><li><strong>CPU 密集型任务</strong>配置<code>尽可能小的线程</code>,如配置 <span class="katex--inline"><span class="katex"><span class="katex-mathml">
     
      
       
        
         
          N
         
         
          
           c
          
          
           p
          
          
           u
          
         
        
        
         +
        
        
         1
        
       
       
        N_{cpu}+1
       
      
     </span><span class="katex-html"><span class="base"><span class="strut" style="height: 0.969438em; vertical-align: -0.286108em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right: 0.10903em;">N</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height: 0.151392em;"><span class="" style="top: -2.55em; margin-left: -0.10903em; margin-right: 0.05em;"><span class="pstrut" style="height: 2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">c</span><span class="mord mathdefault mtight">p</span><span class="mord mathdefault mtight">u</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height: 0.286108em;"><span class=""></span></span></span></span></span></span><span class="mspace" style="margin-right: 0.222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right: 0.222222em;"></span></span><span class="base"><span class="strut" style="height: 0.64444em; vertical-align: 0em;"></span><span class="mord">1</span></span></span></span></span> 个线程的线程池。</li><li><strong>IO 密集型任务</strong>则由于线程并不是一直在执行任务,则<code>配置尽可能多的线程</code>,如<span class="katex--inline"><span class="katex"><span class="katex-mathml">
     
      
       
        
         2
        
        
         ∗
        
        
         
          N
         
         
          
           c
          
          
           p
          
          
           u
          
         
        
       
       
        2*N_{cpu}
       
      
     </span><span class="katex-html"><span class="base"><span class="strut" style="height: 0.64444em; vertical-align: 0em;"></span><span class="mord">2</span><span class="mspace" style="margin-right: 0.222222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right: 0.222222em;"></span></span><span class="base"><span class="strut" style="height: 0.969438em; vertical-align: -0.286108em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right: 0.10903em;">N</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height: 0.151392em;"><span class="" style="top: -2.55em; margin-left: -0.10903em; margin-right: 0.05em;"><span class="pstrut" style="height: 2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">c</span><span class="mord mathdefault mtight">p</span><span class="mord mathdefault mtight">u</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height: 0.286108em;"><span class=""></span></span></span></span></span></span></span></span></span></span>。</li><li><strong>混合型任务</strong>,如果可以拆分,则<code>将其拆分成一个 CPU 密集型任务和一个 IO 密集型任务</code>。只要这<code>两个任务执行的时间相差不是太大</code>,那么<code>分解后执行的吞吐率要高于串行执行的吞吐率</code>;如果这两个任务执行时间相差太大,则没必要进行分解。</li></ol> 
<ul><li><strong>优先级不同的任务</strong>可以使用优先级队列 <code>PriorityBlockingQueue</code> 来处理,它可以让优先级高的任务先得到执行。但是,如果<code>一直有高优先级的任务加入到阻塞队列中</code>,那么<code>低优先级的任务可能永远不能执行</code>。</li><li><strong>执行时间不同的任务</strong>可以交给<code>不同规模的线程池</code>来处理,或者<code>也可以使用优先级队列</code>,让<code>执行时间短的任务先执行</code>。</li><li><strong>依赖数据库连接池的任务</strong>,因为线程提交 SQL 后需要等待数据库返回结果,<code>线程数应该设置得较大</code>,这样才能更好的利用 CPU。</li><li><strong>建议使用有界队列</strong>,有界队列能<code>增加系统的稳定性和预警能力</code>。可以根据需要设大一点,比如几千。<code>使用无界队列</code>,线程池的队列就会越来越大,<strong>有可能会撑满内存,导致整个系统不可用</strong>。</li></ul> 
<h5><a id="__123"></a>② 线程池的监控</h5> 
<ul><li>可以通过线程池提供的参数读线程池进行监控,有以下属性可以使用:</li></ul> 
<ol><li><code>taskCount:</code>线程池需要执行的任务数量,包括已经执行完的、未执行的和正在执行的。</li><li><code>completedTaskCount:</code>线程池在运行过程中<strong>已完成的任务数量</strong>,<code>completedTaskCount &lt;= taskCount</code>。</li><li><code>largestPoolSize:</code>线程池<strong>曾经创建过的最大线程数量</strong>,<code>通过这个数据可以知道线程池是否满过</code>。<strong>如等于线程池的最大大小</strong>,则表示<code>线程池曾经满了</code>。</li><li><code>getPoolSize:</code> 线程池的线程数量。如果<code>线程池不销毁</code>的话,<code>池里的线程不会自动销毁</code>,所以<strong>线程池的线程数量只增不减</strong>。</li><li><code>getActiveCount:</code>获取<strong>活动的</strong>线程数。</li></ol> 
<ul><li>通过<strong>继承线程池</strong>并<strong>重写</strong>线程池的 <code>beforeExecute</code>,<code>afterExecute</code> 和 <code>terminated</code> 方法,我们可以在<code>任务执行前</code>,<code>执行后</code>和<code>线程池关闭前</code>干一些事情。</li><li>如监控任务的<code>平均执行时间</code>,<code>最大执行时间</code>和<code>最小执行时间</code>等。<strong>这几个方法在线程池里是空方法</strong>,如:</li></ul> 
<pre class="prettyprint"><code class="prism language-java has-numbering" οnclick="mdcp.copyCode(event)" style="position: unset;"><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">beforeExecute</span><span class="token punctuation">(</span>Thread t<span class="token punctuation">,</span> Runnable r<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token punctuation">}</span>
<div class="hljs-button {2}" data-title="复制" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.4259&quot;}"></div></code><ul class="pre-numbering" style=""><li style="color: rgb(153, 153, 153);">1</li></ul></pre> 
<h4><a id="3_Java_137"></a>3. Java线程池的常见问题</h4> 
<p><strong>1. 讲讲Java的线程池</strong></p> 
<ul><li><strong>基础讲解:</strong></li></ul> 
<ol><li>以<code>ThreadPoolExecutor</code>为切入点,讲解<code>excute()方法</code>中所体现的<code>Java线程池运行流程</code>。</li><li>工作线程Worker,它的循环工作特点</li><li>如何新建线程池:7个参数(重点在阻塞队列和饱和策略)</li></ol> 
<ul><li><strong>进阶:</strong></li></ul> 
<ol><li>线程池<code>五个状态的特点</code>以及<code>如何进行状态之间的切换</code>:<code>running</code>、<code>shutdown</code>、<code>stop</code>、<code>tidying</code>、<code>terminated</code>。</li><li>如何关闭线程:<code>shutdown方法</code>和<code>shutdownNow方法</code>的特点</li><li>线程池的调优(<code>针对任务的不同特性</code> + <code>建议使用有界队列</code>)</li><li>线程池的<code>监控参数</code>以及<code>可以重写的方法</code>。</li></ol> 
<hr> 
<ul><li>两种主要的线程池类型:普通的线程池<code>ThreadPoolExecutor</code>,支持延迟或周期性执行的任务的线程池<code>ScheduledThreadPoolExcutor</code>。</li><li>讲解<code>ThreadPoolExcutor</code>中5个常用参数+2个不常用参数,包含的三种线程池:创建时的参数、运行的流程、各自适合的场景。</li><li>讲解<code>ScheduledThreadPoolExecutor</code>的阻塞队列的原理、如何更改任务的time。</li><li>提供了五种定义好的线程池,都可以通过<code>Executors</code>工具类去调用,比如<code>Executors.newFixedThreadPool(12)</code></li></ul> 
<p><strong>2. 具体的场景,如果corePoolSize为x,maximumPoolSize为y,阻塞队列为z,第w个任务进来如何分配?</strong></p> 
<p><strong>3. 线程池如何进行调优?</strong></p> 
<ul><li>线程池的调优(<code>针对任务的不同特性</code> + <code>建议使用有界队列</code>)</li></ul> 
<p><strong>4. 线程池中的核心参数,超过核心size怎么处理,队列满怎么处理,拒绝策略有哪些?(比较具体)</strong></p>
                </div><div data-report-view="{&quot;mod&quot;:&quot;1585297308_001&quot;,&quot;dest&quot;:&quot;https://blog.csdn.net/u014454538/article/details/96910729&quot;,&quot;extend1&quot;:&quot;pc&quot;,&quot;ab&quot;:&quot;new&quot;}"><div></div></div>
                <link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/markdown_views-d7a94ec6ab.css" rel="stylesheet">
                <link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/style-f1c5feb645.css" rel="stylesheet">
        </div>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值