线程池

来源:互联网 发布:联想网络同传系统下载 编辑:程序博客网 时间:2024/06/05 00:49

参考

        java自带线程池和队列详解

        线程池的原理及实现

目的

        减少系统在创建和销毁线程时对资源的消耗;防止因为过多创建线程导致系统崩溃。

Executor和ExecutorService

        Executor:是java内置线程池的顶级接口,它只有一个execute()方法,用来执行Runnable类型的任务。
        ExecutorService:继承于Executor,并且声明了管理线程池的方法。在实际意义上,可以认为它是线程池的入口。它里面的常用方法如下:
        shutdown():平缓地关闭线程池。调用该方法后,线程池并不会立即关闭。它会等待线程池中的任务全部结束后才关闭(不管在调用shutdown()时这个任务有没有开始执行)。但是,调用该方法后,线程池不会再接收新的任务。
        shutdownNow():强制关闭线程池,取消所有正在执行的任务,并将没有执行的任务以List集合的形式返回。但要注意:它并不能保证绝对完全地取消正在执行的任务,因为一般的子类都是通过Thread#interrupt()进行操作的。
        isShutdown():在ExecutorService关闭后返回true,否则返回false。
        awaitTermination():在指定timeout时长后检测ExecutorService是否已终止(isTerminated()返回true)。如果终止,返回true。否则返回false。注意,它会阻塞当前线程,直到ExecutorService已经终止或者超时。
        isTerminated():如果ExecutorService关闭后,线程池中的任务已全部结束,就返回true,否则返回false。如果ExecutorService没有关闭,该方法会直接返回fales。

Executors

        工具类。该类中提供了一些静态方法,用于生成一些常用的线程池。常用方法如下:
        newFixedThreadPool():创建指定大小的线程池。线程池中会始终保存着这些线程,即使这些线程都处于空闲状态。它的源码如下:
    public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue<Runnable>());    }
        当某个线程因为意外情况而中断时,系统会重新创建一个线程补充到线程池中。
        newSingleThreadExecutor():创建只含有单个线程的线程池。该线程会串行地执行所有任务。如果该线程因为意外情况而中断,系统会补充一个新线程。
        newCachedThreadPool():创建可缓存的线程池,该线程池的大小无限制。当线程60s不执行任务时,便会被回终止,并且从线程池中移除。当任务到来,而线程池中没有可用线程时,系统会创建新的线程执行任务,并且将新线程添加到线程池中。源码为:
    public static ExecutorService newCachedThreadPool() {        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                      60L, TimeUnit.SECONDS,                                      new SynchronousQueue<Runnable>());    }
        它的第二个参数是Integer.MAX_VALUE,也就是说可以往线程池中不断添加新的线程。而第一个参数为0,说明开始时线程池中并不存在线程,只有当有新任务时,系统才会创建新的线程并且添加到池中。第三、四个参数说明线程可以在线程池中以空闲状态待60s,一旦超过60s便会被移出线程池,进而销毁掉。
        newScheduledThreadPool():创建可定时执行任务的线程池,该线程池的大小无限制。它的参数指的是线程池中时刻保持的线程的数量,即使这些线程处于空闲状态也不会被销毁。

ThreadPoolExecutor

        参考:自带线程池详解
        实现了ExecutorService接口。构造函数中的几个参数的意思为:
        corePoolSize:线程池中保留的线程个数。
        maximumPoolSize:线程池最大容纳线程的个数。
        keepAliveTime:空闲线程结束的超时时间
        unit:keepAliveTime的单位
        workQueue:存放任务的队列

执行过程

        线程池在刚创建时,池中没有一个线程。当有新任务添加后,如果线程池中线程的数量少于corePoolSize,那么会创建新线程执行任务;如果大于或等于corePoolSize,但workQueue未满,会将任务加到workQueue中;如果workQueue已满,且线程数量小于maximunPoolSize,那么会创建新线程执行;如果大于或等于maximumPoolSize,那么线程池会抛出异常。
        如果某个线程空闲时间超过了keepAliveTime,并且当前的线程总数超过了corePoolSize,那么该线程会被停掉。因此,当任务全部执行完毕后,线程池中线程的数量会降低到corePoolSize个。

任务队列

        它是BlockingQueue<Runnable>类型,常用的两个子类为LinkedBlockingQueue与ArrayBlockingQueue。相较于前者,后者的存储数量是有限的,在创建该对象时必须传入一个capacity值。
        当我们的队列使用LinkedBlockingQueue时,maximumPoolSize的值就没有意义,因为队列是无限的,也就不存在队列存满的情况了。
        例如在Executors.newFixedThreadPool()中,它使用LinkedBlockingQueue作为任务队列,就保证了无论添加多少个任务,线程池中线程的个数永远是newFixedThreadPool传入的参数,这样就相当于创建了一个固定线程数量的线程池。

源码

public void execute(Runnable command) {if (command == null)throw new NullPointerException();if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {/* * 进入到该判断,说明有出现以下两种情况中的一种: (1)当前任务执行线程数量不小于核心线程数(核心线程已经用完) * (2)创建新的线程失败(核心线程没有用完,但创建失败) 当发生上面两种情况时,便会进行如下操作。 首先判断当前线程池的状态。 * 如果处于RUNNING状态,便尝试着将任务添加到任务队列中。 * 如果不处于RUNNING状态或者添加失败,并尝试依据最大线程数量进行再次操作。 */if (runState == RUNNING && workQueue.offer(command)) {/* * 能走到该判断内,说明command已经被添加到任务队列中了。 * 此时会进行再一次的判断。这是因为虽然任务被添加到任务队列,但它不能被执行 * 要么它不应该执行,在它添加进任务队列之前在别的地方关闭了该线程池 * ——runState!=RUNNING,毕竟添加进队列也需要一段时间 要么它没办法被执行,poolSize == 0。 * 所以为了保证新添加的任务能被处理,必须进行再一次的判断。 */if (runState != RUNNING || poolSize == 0)ensureQueuedTaskHandled(command);}/* * 如果不处于RUNNING状态,或者添加失败(有可能是因为任务队列已满)。便会根据根据线程池中线程数量与最大线程数的关系进行操作。 * 一旦连最大线程数都处理不了(即addIfUnderMaximunPoolSize()返回false),那么线程池拒绝了该任务, * 便会调用设置的拒绝策略。如果处理了,那么该任务就被执行。 */else if (!addIfUnderMaximumPoolSize(command))reject(command); // is shutdown or saturated}}
        从上面的方法也可以看出,线程池对任务的处理优先级为:核心线程>任务队列>最大线程。核心线程未满时,新建新线程用来执行任务;核心线程已满时,便将新任务添加到任务队列中;任务队列已满时,便会调用设置的拒绝策略操作该任务。
addIfUnderCorePoolSize

private boolean addIfUnderCorePoolSize(Runnable firstTask) {Thread t = null;final ReentrantLock mainLock = this.mainLock;mainLock.lock();//加锁try {//判断是否应该创建新的线程if (poolSize < corePoolSize && runState == RUNNING)t = addThread(firstTask);//核心线程未满,且处于RUNNINT状态,就创建新线程执行任务} finally {mainLock.unlock(); /* 解除锁定,说明关键线程的修改或读取已经完毕 */}if (t == null)//如果创建新线程未成功return false;t.start();//如果创建新线程成功,便启动新线程。return true;}
        从上面可以看出addIfUnderCorePoolSize()的主要作用时:在核心线程未满的前提下,通过创建新的线程来扩展线程池,并用新线程执行任务。这也可以看出:对新提交的任务首先需要经过核心线程的判断,如果核心线程未处理再进行任务队列和最大线程的判断。
        addIfUnderMaximumPoolSize()与之类似,略。

   


0 0
原创粉丝点击