Java Concurrency 2: ThreadPool

来源:互联网 发布:mysql 默认值约束 编辑:程序博客网 时间:2024/06/14 01:43

基本概念

《设计模式之禅》将对象池作为一种新的设计模式来看待,在重复生成对象的操作影响性能的时候适合将对象池化,这样看线程池算是对象池模式的一个典型应用了,其他应用还有DBCP、C3P0等数据库连接池,实际开发中如果使用对象池,可以用common-pool工具包来辅助实现,方便快捷。

使用线程池的好处:

  • 降低资源消耗
  • 提高响应速度
  • 提高线程的可管理性

工厂类Executors可以建立几种常用的线程池:

    public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue());    }    public static ExecutorService newCachedThreadPool() {        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                      60L, TimeUnit.SECONDS,                                      new SynchronousQueue());    }        public static ExecutorService newSingleThreadExecutor() {        return new FinalizableDelegatedExecutorService            (new ThreadPoolExecutor(1, 1,                                    0L, TimeUnit.MILLISECONDS,                                    new LinkedBlockingQueue()));    }

另外还有workStealingPool和scheduledThreadPool,其实都是调用了ThreadPoolExecutor类来新建线程池,下面分析下这个类。

ThreadPoolExecutor参数
先看看jdk源码中对参数的说明:

   /**     * Creates a new {@code ThreadPoolExecutor} with the given initial     * parameters.     *     * @param corePoolSize the number of threads to keep in the pool, even     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set     * @param maximumPoolSize the maximum number of threads to allow in the     *        pool     * @param keepAliveTime when the number of threads is greater than     *        the core, this is the maximum time that excess idle threads     *        will wait for new tasks before terminating.     * @param unit the time unit for the {@code keepAliveTime} argument     * @param workQueue the queue to use for holding tasks before they are     *        executed.  This queue will hold only the {@code Runnable}     *        tasks submitted by the {@code execute} method.     * @param threadFactory the factory to use when the executor     *        creates a new thread     * @param handler the handler to use when execution is blocked     *        because the thread bounds and queue capacities are reached     * @throws IllegalArgumentException if one of the following holds:<br>     *         {@code corePoolSize < 0}<br>     *         {@code keepAliveTime < 0}<br>     *         {@code maximumPoolSize <= 0}<br>     *         {@code maximumPoolSize < corePoolSize}     * @throws NullPointerException if {@code workQueue}     *         or {@code threadFactory} or {@code handler} is null     */    public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              ThreadFactory threadFactory,                              RejectedExecutionHandler handler) {        if (corePoolSize < 0 ||            maximumPoolSize <= 0 ||            maximumPoolSize < corePoolSize ||            keepAliveTime < 0)            throw new IllegalArgumentException();        if (workQueue == null || threadFactory == null || handler == null)            throw new NullPointerException();        this.corePoolSize = corePoolSize;        this.maximumPoolSize = maximumPoolSize;        this.workQueue = workQueue;        this.keepAliveTime = unit.toNanos(keepAliveTime);        this.threadFactory = threadFactory;        this.handler = handler;    }
  • corePoolSize—核心线程数
    核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。
    核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。
  • maximumPoolSize—最大线程数
    当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。
  • workQueue—任务队列
    保存等待执行的任务的阻塞队列,可以选择一下几种:
    • ArrayBlockingQueue
      基于数组的有界阻塞队列,FIFO。
    • LinkedBlockingQueue
      基于链表的阻塞队列,FIFO;静态工厂方法Executors.newFixedThreadPool()即使用该队列。
    • SynchronousQueue
      不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入会一直处于阻塞状态,吞吐量通常会高于LinkedBlockingQueue。静态工厂方法Executors.newCachedThreadPool()即使用该队列。
    • PriorityBlockingQueue
  • keepAliveTime
    当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。
  • allowCoreThreadTimeout
    是否允许核心线程空闲退出,默认值为false。
  • queueCapacity
    任务队列容量。从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。

任务提交

  • submit()
    需要返回信息,其内部还是调用的execute()
  • execute()

线程池按以下顺序执行任务:

  • 当线程数小于核心线程数时,创建线程。
  • 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
  • 当线程数大于等于核心线程数,且任务队列已满
    • 若线程数小于最大线程数,创建线程
    • 若线程数等于最大线程数,抛出异常,拒绝任务

参数设置
实际应用场景一般不会直接调用Executors直接这几种线程,而是根据响应需求和系统资源综合考虑,

IO密集型和CPU密集型
我们可以把任务分为计算密集型和IO密集型。
计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等。这种类型的任务越多,花在任务切换的时间就越多,导致CPU执行任务的时间减少。所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。
IO密集型,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度),涉及到网络、磁盘IO的任务都是IO密集型任务。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。

参考:
http://blog.csdn.net/miclung/article/details/7231553
http://www.cnblogs.com/jinzhiming/p/5120623.html
http://www.blogjava.net/bolo/archive/2015/01/20/422296.html

原创粉丝点击