ThreadPool技术分析

来源:互联网 发布:php 输出错误信息 编辑:程序博客网 时间:2024/06/06 10:54

ThreadPool技术分析

  • ThreadPool技术分析

ThreadPool是我们进行并发开发中肯定要使用的一个强大工具,因为创建线程不仅要在VM栈分配新的线程执行栈,而且要调native方法对线程做创建和初始化,是个相当占用资源的事情。如果宿主机操作系统使用的KLT的话,不断的创建和销毁线程对程序的性能影响更加严重。

本文将以java.util.concurrent.ThreadPoolExecutor为例,简单地分析一下ThreadPool的实现原理。

首先,我们先看看java.util.concurrent.ThreadPoolExecutor#execute

/*** Executes the given task sometime in the future.  The task* may execute in a new thread or in an existing pooled thread.** If the task cannot be submitted for execution, either because this* executor has been shutdown or because its capacity has been reached,* the task is handled by the current {@code RejectedExecutionHandler}.** @param command the task to execute* @throws RejectedExecutionException at discretion of*         {@code RejectedExecutionHandler}, if the task*         cannot be accepted for execution* @throws NullPointerException if {@code command} is null*/public void execute(Runnable command) {    if (command == null)        throw new NullPointerException();    /*    * Proceed in 3 steps:    *    * 1. If fewer than corePoolSize threads are running, try to    * start a new thread with the given command as its first    * task.  The call to addWorker atomically checks runState and    * workerCount, and so prevents false alarms that would add    * threads when it shouldn't, by returning false.    *    * 2. If a task can be successfully queued, then we still need    * to double-check whether we should have added a thread    * (because existing ones died since last checking) or that    * the pool shut down since entry into this method. So we    * recheck state and if necessary roll back the enqueuing if    * stopped, or start a new thread if there are none.    *    * 3. If we cannot queue task, then we try to add a new    * thread.  If it fails, we know we are shut down or saturated    * and so reject the task.    */    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);}

我可以看到,实现上虽然方法名叫execute,实际上只是把Runnable对象加入了一个private final BlockingQueue<Runnable> workQueue;, 等待被执行。

java.util.concurrent.ThreadPoolExecutor#runWorker才是真正运行Runnable对象的方法:

/*** Main worker run loop.  Repeatedly gets tasks from queue and* executes them, while coping with a number of issues:** 1. We may start out with an initial task, in which case we* don't need to get the first one. Otherwise, as long as pool is* running, we get tasks from getTask. If it returns null then the* worker exits due to changed pool state or configuration* parameters.  Other exits result from exception throws in* external code, in which case completedAbruptly holds, which* usually leads processWorkerExit to replace this thread.** 2. Before running any task, the lock is acquired to prevent* other pool interrupts while the task is executing, and then we* ensure that unless pool is stopping, this thread does not have* its interrupt set.** 3. Each task run is preceded by a call to beforeExecute, which* might throw an exception, in which case we cause thread to die* (breaking loop with completedAbruptly true) without processing* the task.** 4. Assuming beforeExecute completes normally, we run the task,* gathering any of its thrown exceptions to send to afterExecute.* We separately handle RuntimeException, Error (both of which the* specs guarantee that we trap) and arbitrary Throwables.* Because we cannot rethrow Throwables within Runnable.run, we* wrap them within Errors on the way out (to the thread's* UncaughtExceptionHandler).  Any thrown exception also* conservatively causes thread to die.** 5. After task.run completes, we call afterExecute, which may* also throw an exception, which will also cause thread to* die. According to JLS Sec 14.20, this exception is the one that* will be in effect even if task.run throws.** The net effect of the exception mechanics is that afterExecute* and the thread's UncaughtExceptionHandler have as accurate* information as we can provide about any problems encountered by* user code.** @param w the worker*/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 pool is stopping, ensure thread is interrupted;            // if not, ensure thread is not interrupted.  This            // requires a recheck in second case to deal with            // shutdownNow race while clearing interrupt            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);    }}

可以清楚地看看,ThreadPool实际是上是java.util.concurrent.ThreadPoolExecutor.Worker的Pool, 而不是由我们的Runnable创建的Thread组成的Pool, 同时Worker Thread明显也不会在Runnable执行完以后销毁。不过没有关系,因为Runnable的task是在local scope中执行的,其创建的对象在表现上面与在真实的单独Thread中运行一般没有什么不同,除非有与Thread相关的操作存在,比如有ThreadLocal变量。

原创粉丝点击