# 线程池相关知识点

<ol>
 	<li></li>
 	<li> 从阻塞队列开始说起
<ol>
 	<li>在操作阻塞队列时，如果队列内容为空，那么消费线程会被阻塞；如果队列已经满了，那么生产线程将会阻塞</li>
 	<li>阻塞队列的分类
<ol>
 	<li>ArrayBlockingQueue
<ol>
 	<li>有界队列</li>
 	<li>底层为Array形式存储</li>
 	<li>如果所有的任务都是按顺序执行，不存在“插队”和从队伍中离开，则适合使用ArrayBlockingQueue</li>
</ol>
</li>
 	<li>LinkedBlockingQueue
<ol>
 	<li>无界队列</li>
 	<li>底层为链表形式存储</li>
 	<li>如果存在“插队”，和从队伍中离开的情况则适合使用LinkedBlockingQueue</li>
</ol>
</li>
 	<li>SynchronousQueue
<ol>
 	<li>只能有一个元素</li>
</ol>
</li>
</ol>
</li>
 	<li>阻塞队列的使用示例略</li>
</ol>
</li>
 	<li>使用线程的优势
<ol>
 	<li>减少了线程创建，销毁的性能消耗，复用了线程</li>
 	<li>充分利用cpu的内核（相对于单线程），省略了上下文的切换</li>
 	<li>提高了线程的可管理性，线程池可以对线程统一分配，调优和监控</li>
 	<li>由于减少了线程的创建时间，所以响应速度会有所提高</li>
</ol>
</li>
 	<li>线程池的几个参数作用
<pre><code>public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue&lt;Runnable&gt; workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
</code></pre>
<ol>
 	<li>corePoolSize:线程池的核心线程数量，如果线程池中的线程数量小于这个数，每次执行execute()方法会创建新的线程；如果正在运行的线程数大于corePoolSize，当有新的任务时候，会将任务加入到阻塞队列</li>
 	<li>当任务已经填满阻塞对列，则会创建新的线程去执行任务，直到线程数到达maximumPoolSize；maximumPoolSize就是线程池中可以创建的最大线程数；如果线程池中的线程已经超过corePoolSize，当队列中的任务逐渐减少时，线程池的中的线程也会销毁。</li>
 	<li>当线程超过keepAliveTime没有执行任务时，线程池中的线程就会销毁，直到线程数量减少到 corePoolSize；如果设置了allowCoreThreadTimeOut，核心线程也会销毁，线程池中线程数量会减少到0</li>
 	<li>unit参数就是线程超时销毁的时间单位,如TimeUnit.MILLISECONDS</li>
 	<li>workQueue就是暂存任务的阻塞队列</li>
 	<li>threadFactory是创建线程的工厂类，一般使用默认的工厂类，Executors.defaultThreadFactory()</li>
 	<li>handler 线程池的拒绝策略
<ol>
 	<li>在线程池中线程的数量已经达到maximumPoolSize，并且阻塞对列中已经放满了待执行的任务，那么线程池就会执行拒绝策略，拒绝新的任务</li>
 	<li>常见的拒绝策略
<ol>
 	<li>new ThreadPoolExecutor.CallerRunsPolicy()
<ol>
 	<li>将新的任务交给调用者执行</li>
</ol>
</li>
 	<li>new ThreadPoolExecutor.AbortPolicy()
<ol>
 	<li>直接抛出RejectedExecutionException异常</li>
</ol>
</li>
 	<li>new ThreadPoolExecutor.DiscardPolicy()
<ol>
 	<li>丢弃调新的任务，但不抛出异常</li>
</ol>
</li>
 	<li>new ThreadPoolExecutor.DiscardOldestPolicy()
<ol>
 	<li>丢弃队列中最老的任务，但不抛出异常</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
 	<li>Executors提供的几个线程池就像Collectionss提供针对Collection的工具一样，Executors提供了针对Executor的各种工具方法，其中最常见的便是·创建线程的工具方法，有四种：
<pre><code>//创建n个线程，n为Integer.MAX_VALUE
Executors.newCachedThreadPool();
//有固定线程数的线程池，corePoolSize和maxPoolSize相等
Executors.newFixedThreadPool(10);
//有定时或周期执行的线程池
Executors.newScheduledThreadPool(10);
//只有一个线程的线程池 corePoolSize = maxPoolSize = 1
Executors.newSingleThreadExecutor();
</code></pre>
特别注意：在生产项目中一般不使用Executors提供的这几个线程池，因为它底层的阻塞队列是无限的，有可能会造成OOM</li>
 	<li>带返回值的线程池
<ol>
 	<li>如果要获取子线程返回的结果，那么要实现Callable接口</li>
 	<li>FutureTask类实现了Runnable接口，并且拥有包含Callable参数的构造函数FutureTask(Callable task)，可以通过futureTask去执行要获得返回结果的子线程</li>
 	<li>一个执行实现Callable接口的示例
<pre><code>public class ThreadPoolDemo {


    public static  void main(String[] args){

          TaskWithReturn taskWithReturn = new TaskWithReturn();
        FutureTask&lt;Integer&gt; taskWithReturnFutureTask = new FutureTask&lt;Integer&gt;(taskWithReturn);
        Thread thread = new Thread(taskWithReturnFutureTask);
        thread.start();
    }

    private static class TaskWithReturn implements Callable&lt;Integer&gt; {
        @Override
        public Integer call() throws Exception {
            System.out.println(Thread.currentThread().getName());
            return 1234;
        }
    }
}
</code></pre>
</li>
</ol>
</li>
 	<li>ThreadPoolExecutor的execute方法和submit方法区别
<ol>
 	<li>execute只能执行实现了Runnable接口的类；submit可以执行实现了Runnable接口的类也可以执行实现了Callable接口的类</li>
 	<li>submit方法可以通过返回值Future获取到子线程的执行结果和异常信息， 如果线程正确执行完毕future.get()获取到线程的返回结果，如果子线程抛出异常future.get()获取到异常信息</li>
</ol>
</li>
</ol>
&nbsp;

二、 线程池解决的什么问题

线程池解决的核心问题就是资源管理问题。在并发环境下，系统不能够确定在任意时刻中，有多少任务需要执行，有多少资源需要投入。这种不确定性将带来以下若干问题：
<ol>
 	<li>频繁申请/销毁资源和调度资源，将带来额外的消耗，可能会非常巨大。</li>
 	<li>对资源无限申请缺少抑制手段，易引发系统资源耗尽的风险。</li>
 	<li>系统无法合理管理内部的资源分布，会降低系统的稳定性。</li>
</ol>
