Java线程池核心类ThreadPoolExecutor

来源:互联网 发布:淘宝婚庆四件套 编辑:程序博客网 时间:2024/06/08 00:37

  通常,当需要同时处理的任务比较多时,为了避免为每个任务开一个线程(因为这样会导致频繁的线程开启和销毁,开销较大),采用线程池技术来进行线程资源的复用。
  在应用中,我们通常使用Executors类提供的静态方法来使用线程池:

ExecutorService exec0 = Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUEExecutorService exec1 = Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池ExecutorService exec2 = Executors.newFixedThreadPool(int);  //创建固定容量大小的缓冲池

  其中ExecutorService是一个接口,实现线程池的核心是一个ThreadPoolExecutor。将任务比如一个Runnable,或者一个Callable作为参数传入submit方法即完成任务向线程池的提交,将在线程池中自动执行该任务。返回参数可以是一个Future<?>对象。

  那么线程池内部具体是怎么实现的,诸如ExecutorService接口和和TheadPoolExecutor又有什么关系呢?下面通过jdk中的源码来学习一下。

与ThreadPoolExecutor类相关的接口和类的关系

public class ThreadPoolExecutor extends AbstractExecutorService{...};public abstract class AbstractExecutorService implements ExecutorService {...}public interface ExecutorService extends Executor{...}public interface Executor {    void execute(Runnable command);}

  可以清楚地看到它们之间的关系,Executor 接口只声明了一个方法,而ExecutorService接口在其基础上扩展出了一些方法:

public interface ExecutorService extends Executor {    void shutdown();    List<Runnable> shutdownNow();    <T> Future<T> submit(Callable<T> task);    <T> Future<T> submit(Runnable task, T result);    Future<?> submit(Runnable task);    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)        throws InterruptedException;    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,                                  long timeout, TimeUnit unit)        throws InterruptedException;    <T> T invokeAny(Collection<? extends Callable<T>> tasks)        throws InterruptedException, ExecutionException;    <T> T invokeAny(Collection<? extends Callable<T>> tasks,                    long timeout, TimeUnit unit)        throws InterruptedException, ExecutionException, TimeoutException;}

  其中的submit是我们比较关注的方法,也就是核心的提交任务的方法。
  AbstractExecutorService则实现了上述方法,比如如下三个版本的submit方法实现:

    public Future<?> submit(Runnable task) {        if (task == null) throw new NullPointerException();        RunnableFuture<Void> ftask = newTaskFor(task, null);        execute(ftask);        return ftask;    }    public <T> Future<T> submit(Runnable task, T result) {        if (task == null) throw new NullPointerException();        RunnableFuture<T> ftask = newTaskFor(task, result);        execute(ftask);        return ftask;    }    public <T> Future<T> submit(Callable<T> task) {        if (task == null) throw new NullPointerException();        RunnableFuture<T> ftask = newTaskFor(task);        execute(ftask);        return ftask;    }

  可以看到,提交任务的方式可以是Runnable也可以是Callable,并且为了最终可以返回参数,同意采用了将task包装成RunnableFuture,该接口双重继承了Runnable接口和Future接口。然后就是execute方法的调用,execute方法的实现交给了继承AbstractExecutorService类的子类来完成,这里就是我们要学习的核心类:ThreadPoolExecutorService。

核心类ThreadPoolExecutorService

  其核心的构造器接口如下:

    public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              ThreadFactory threadFactory,                              RejectedExecutionHandler handler)

  其中corePoolSize、maximumPoolSize分别表示核心池的大小,最大线程池的大小。keepAliveTime和空闲线程的存活时间有关,workQueue是一个阻塞队列,用于缓存任务,后面两个参数分别用于产生线程和拒绝服务的处理方式。

  首先,我们想搞清楚当一个任务通过线程池submit后,是怎么被执行的而submit实际上也就是调用了execute方法,所以execute就是线程池最核心的部分。
这篇博客中介绍的比较清楚了,但是我看了下我jdk版本是1.8.0_40,发现execute的实现和文章中介绍的有差异,细看了一下基本实现思路应该还是一样的。
在我的jdk版本中,execute方法是这样的:

    public void execute(Runnable command) {        if (command == null)            throw new NullPointerException();        int c = ctl.get();        if (workerCountOf(c) < corePoolSize) {            if (addWorker(command, true))                return;            c = ctl.get();        }        if (isRunning(c) && workQueue.offer(command)) {            int recheck = ctl.get();            if (! isRunning(recheck) && remove(command))                reject(command);            else if (workerCountOf(recheck) == 0)                addWorker(null, false);        }        else if (!addWorker(command, false))            reject(command);    }

  先是判断当前线程池中的线程数和corePoolSize的大小,如果小于,则直接为新进的任务增加一个工作线程addWorker(command, true),并返回。否则尝试将任务添加到阻塞队列中,后面是检查一些异常情况,如是否外部将线程池停止isRunning(recheck)等。最后还有拒绝服务的情况。

  下面我们再来看addWorker

    private boolean addWorker(Runnable firstTask, boolean core) {        retry:        for (;;) {            int c = ctl.get();            int rs = runStateOf(c);            // Check if queue empty only if necessary.            if (rs >= SHUTDOWN &&                ! (rs == SHUTDOWN &&                   firstTask == null &&                   ! workQueue.isEmpty()))                return false;            for (;;) {                int wc = workerCountOf(c);                if (wc >= CAPACITY ||                    wc >= (core ? corePoolSize : maximumPoolSize))                    return false;                if (compareAndIncrementWorkerCount(c))                    break retry;                c = ctl.get();  // Re-read ctl                if (runStateOf(c) != rs)                    continue retry;                // else CAS failed due to workerCount change; retry inner loop            }        }        boolean workerStarted = false;        boolean workerAdded = false;        Worker w = null;        try {            w = new Worker(firstTask);            final Thread t = w.thread;            if (t != null) {                final ReentrantLock mainLock = this.mainLock;                mainLock.lock();                try {                    // Recheck while holding lock.                    // Back out on ThreadFactory failure or if                    // shut down before lock acquired.                    int rs = runStateOf(ctl.get());                    if (rs < SHUTDOWN ||                        (rs == SHUTDOWN && firstTask == null)) {                        if (t.isAlive()) // precheck that t is startable                            throw new IllegalThreadStateException();                        workers.add(w);                        int s = workers.size();                        if (s > largestPoolSize)                            largestPoolSize = s;                        workerAdded = true;                    }                } finally {                    mainLock.unlock();                }                if (workerAdded) {                    t.start();                    workerStarted = true;                }            }        } finally {            if (! workerStarted)                addWorkerFailed(w);        }        return workerStarted;    }

  这个方法用于增加工作线程,所以传入的参数其中的任务叫firstTask, 可以看到前面一部分还用到了跳转指令,retry点。for(;;){}一部分还没看太懂,大致可以看出是对非正常执行情况的处理,都是一些跳转或返回。
  后面的try块是我们关注的重点,首先将firstTask包装成了一个Worker类,还获取了Worker的线程得到t,向workers这个HashSet中添加工作线程,最后调用t.start(); 来启一个工作线程。
  我们再来看Worker类:

    private final class Worker        extends AbstractQueuedSynchronizer        implements Runnable{...}

  再来看其核心run()方法,

public void run() {            runWorker(this);        }

  调用了ThreadPoolExecutor的runWorker方法,

    final void runWorker(Worker w) {        Thread wt = Thread.currentThread();        Runnable task = w.firstTask;        w.firstTask = null;        w.unlock(); // allow interrupts        boolean completedAbruptly = true;        try {            while (task != null || (task = getTask()) != null) {                w.lock();                if ((runStateAtLeast(ctl.get(), STOP) ||                     (Thread.interrupted() &&                      runStateAtLeast(ctl.get(), STOP))) &&                    !wt.isInterrupted())                    wt.interrupt();                try {                    beforeExecute(wt, task);                    Throwable thrown = null;                    try {                        task.run();                    } catch (RuntimeException x) {                        thrown = x; throw x;                    } catch (Error x) {                        thrown = x; throw x;                    } catch (Throwable x) {                        thrown = x; throw new Error(x);                    } finally {                        afterExecute(task, thrown);                    }                } finally {                    task = null;                    w.completedTasks++;                    w.unlock();                }            }            completedAbruptly = false;        } finally {            processWorkerExit(w, completedAbruptly);        }    }

  到这个方法执行时,工作线程已经启动了,进入这个方法后就不断在任务缓冲队列中提取任务执行,执行的过程中对该Worker加锁,直到任务队列为空。

  注意到最后的finally语句块中的processWorkerExit(w, completedAbruptly);,查看源代码

    private void processWorkerExit(Worker w, boolean completedAbruptly) {        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted            decrementWorkerCount();        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();        try {            completedTaskCount += w.completedTasks;            workers.remove(w);        } finally {            mainLock.unlock();        }        tryTerminate();        int c = ctl.get();        if (runStateLessThan(c, STOP)) {            if (!completedAbruptly) {                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;                if (min == 0 && ! workQueue.isEmpty())                    min = 1;                if (workerCountOf(c) >= min)                    return; // replacement not needed            }            addWorker(null, false);        }    }

  可以看到,从workers中将移出该工作线程。还可以看到allowCoreThreadTimeOut参数对行为的影响:该参数为false,则对core threads在即使空闲的情况下也不会被杀死,如果为true,则空闲的线程在一定的timeout后将被系统回收。在程序中就是若为false,则min=corePoolSize,如果当前剩余的工作线程大于等于该值,则不需要添加idle线程,否则需要增加一个idle线程,因为刚刚kill了一个工作完成的线程。

  另外,还有线程池的关闭,shutdown()和shutdownNow(),前者不会立即终止线程,而是等待任务缓冲队列中的的任务被完全执行后在结束,但是期间不再接受新的任务。后者则是立即终止所有的线程,并且清空缓冲队列,返回尚未执行的任务。

应用

  一般使用时,我们不是直接使用ThreadPoolExecutor类,而是通过文章开头时介绍的Executors工具类的静态方法来获得该类的实例对象。这就避免了繁杂的参数配置可能导致的潜在错误。

如何配置线程池的大小问题

  一个策略就是根据任务的类型来配置线程池的大小:
  对CPU密集型任务,我们采用Ncpu+1,对IO密集型任务,我们采用2*Ncpu。

0 0
原创粉丝点击