Java线程池

来源:互联网 发布:相机cf卡数据恢复 编辑:程序博客网 时间:2024/05/08 08:57

线程池的关闭


我们可以通过调用线程池的shutdown方法或者shutdownNow方法来关闭线程池,其原理是遍历线程池中的所有工作线程,然后逐个调用线程的interrupt方法来中断线程。这也就意味着无法响应中断的线程可能就无法终止。

shutdown方法按顺序中断之前已提交的任务,同时拒绝接收新的任务。这个方法不会等待之前已提交任务执行完成,可通过调用awaitTermination方法去完成这种功能。已shut down的线程池再调用此方法没有任何影响。

shutdownNow方法尝试停止所有正在执行的任务,终止正在等待的任务,并返回所有正在等待的任务。
只要调用了以上俩方法中任何一个,isShutdown方法都会返回true。当所有任务都已经终止后,才表示线程池关闭成功,此时isTerminated方法会返回true。



线程池分析

线程池的工作流程如下:


从图中我们可以看出,当一个任务被提交到线程池后,线程池的处理流程如下:

  1. 首先判断基本线程池有没有满?如果没满,则创建一个线程来执行任务,否则进入下一流程;
  2. 判断工作队列是否已满?如果没满,将任务存储在工作队列中,否则进入下一流程;
  3. 判断线程池是否已满?如果没满,并且当前线程池的工作线程的数量小于最大线程池数量,则创建新线程,否则提交给饱和处理策略来处理这个任务。

ThreadPoolExecutor的线程池处理流程的源码如下:

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);}
进入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;}
可以看到,在线程池中创建线程时,会将任务封装成工作线程Worker。并且当工作线程Worker执行完任务之后,会将其添加到workers集合中。在Worker的run方法中,将会调用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 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);        }}
由上可以看到,Worker将会通过循环不断从任务队列中获取任务执行。




配置线程池

通常配置线程池需要考虑几个方面:

  • 任务的性质:CPU密集型,IO密集型还是混合型任务;
  • 任务的优先级;
  • 任务执行时间长短;
  • 任务是否依赖于其它的系统资源;
CPU密集型的任务应使用较少的线程来执行任务,如线程池中的线程数量配置为cups+1;IO密集型任务由于线程并不是一直在执行任务,而是有很多时间花费在IO等待上,导致了CPU资源的浪费,此时应该配置尽可能多的线程数量,比如cpus*2;对于混合型的任务,如果可以拆分,则将其拆分为CPU密集型和IO密集型(前提是两者执行任务的时间相差不大,否则没必要去拆分)。

可通过Runtime.getRuntime().availableProcessors()方法获取当前机器的CPU数量。

优先级不同的任务可以将其工作队列设为优先级队列PriorityBlockingQueue。



监控线程池


可通过继承线程池并重写线程池的一些方法,比如beforeExecute,afterExecute以及terminated等方法,在任务执行前,任务执行后以及线程池关闭后实现一些自己的逻辑,如监控任务的平均执行时间等等。这些方法都是空的,且为protected,源码如下:

protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }
protected void beforeExecute(Thread t, Runnable r) { }

此外线程池里还有一些属性也可以在监控时使用,比如taskCount表示线程池需要持续任务的数量等。


参考资料:

  • JDK8源码
  • Java并发编程实战

1 0