Java中的线程池——ThreadPoolExecutor的原理

1 线程池的处理流程
向线程池提交一个任务后,它的主要处理流程如下图所示

一个线程从被提交(submit)到执行共经历以下流程:

线程池判断核心线程池里是的线程是否都在执行任务,如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下一个流程
线程池判断工作队列是否已满。如果工作队列没有满,则将新提交的任务储存在这个工作队列里。如果工作队列满了,则进入下一个流程。
线程池判断其内部线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已满了,则交给饱和策略来处理这个任务。
线程池在执行excute方法时,主要有以下四种情况

1 如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(需要获得全局锁)
2 如果运行的线程等于或多于corePoolSize ,则将任务加入BlockingQueue
3 如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(需要获得全局锁)
4 如果创建新线程将使当前运行的线程超出maxiumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

线程池采取上述的流程进行设计是为了减少获取全局锁的次数。在线程池完成预热(当前运行的线程数大于或等于corePoolSize)之后,几乎所有的excute方法调用都执行步骤2。

2 线程池的源码分析
2.1 定义的几个变量

 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

在分析源码前有必要理解一个变量ctl。这是Java大神们为了把工作线程数量和线程池状态放在一个int类型变量里存储而设置的一个原子类型的变量。 在ctl中,低位的29位表示工作线程的数量,高位用来表示RUNNING、SHUTDOWN、STOP等状态。 因此一个线程池的数量也就变成了(2^29)-1,大约500 million,而不是(2^31)-1,2billion。上面定义的三个方法只是为了计算得到线程池的状态和工作线程的数量。

2.2 Execute 方法提交任务

2.2.1 Execute方法

public void execute(Runnable command) {
     //如果提交了空的任务 抛出异常
        if (command == null)
            throw new NullPointerException();
     int c = ctl.get();//获取当前线程池的状态
     //检查当前工作线程数量是否小于核心线程数量
        if (workerCountOf(c) < corePoolSize) {
        //通过addWorker方法提交任务
            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方法中。在这里面看到了recheck这样的变量,这是在执行了一些动作失败后再次检查线程池的状态,因为在这期间可能有线程池关闭获得线程池饱和等状态的改变。

2.2.2 addWorker方法

这个方法是任务提交的一个核心方法。在里面完成了状态检查、新建任务、执行任务等一系列动作。它有两个参数,第一个参数是提交的任务,第二个参数是一个标识符,标识在检查工作线程数量的时候是应该与corePoolSize对比还是应该maximumPoolSize对比。

private boolean addWorker(Runnable firstTask, boolean core) {
            retry:
//死循环更新状态
            for (;;) {
                    int c = ctl.get();
                    int rs = runStateOf(c);//获取运行状态

        //检查线程池是否处于关闭状态
                    if (rs >= SHUTDOWN &&
                            ! (rs == SHUTDOWN &&
                                 firstTask == null &&
                                 ! workQueue.isEmpty()))
                            return false;

                    for (;;) {
        //获取当前工作线程数量
                            int wc = workerCountOf(c);
    //如果已经超过corePoolSize获取maximumPoolSize 返回false
                            if (wc >= CAPACITY ||
                                    wc >= (core ? corePoolSize : maximumPoolSize))
                                    return false;
    //CAS增加一个工作线程
                            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();
        //添加工作这到hashset中保存
                                            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;
    }

这个方法可以分为两个阶段来看,第一个阶段是判断是否有必要新增一个工作线程,如果有则利用CAS更新工作线程的数量;第二部分是将提交的任务封装成一个工作线程Worker然后加入到线程池的容器中,开始执行新提交的任务。这个Worker在执行完任务后,还会循环地获取工作队列里的任务来执行。下面来看一下Worker的构造方法就能更好地理解上面的代码了
/**

  • Creates with given first task and thread from ThreadFactory.
  • @param firstTask the first task (null if none)
    */
    Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
    }

2.2.3?runWorker方法

在addWorker方法快要结束的地方,调用了t.start()方法,我们知道它实际执行的就是Worker对象的run()方法,而worker的run()方法是这样定义的:
/* Delegates main run loop to outer runWorker /
public void run() {
runWorker(this);
}
它实际上是将自己委托给线程池的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 {
    //不断地从blockingQueue获取任务
        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方法
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
        //调用Runable的run方法
                    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 {
        // 执行aferExecute方法
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

这个方法呢也比较好理解,它在不断执行我们提交的任务的run方法。而这个任务可能是我们新提交的,也有可能是从等待队列中获取的。这样就实现了线程池的完成逻辑。

原文地址:http://blog.51cto.com/13981400/2307622

时间: 2024-07-31 09:23:36

Java中的线程池——ThreadPoolExecutor的原理的相关文章

《Java并发编程的艺术》 第9章 Java中的线程池

第9章 Java中的线程池 在开发过程中,合理地使用线程池能带来3个好处: 降低资源消耗.通过重复利用已创建的线程 降低线程创建和销毁造成的消耗. 提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行. 提高线程的可管理性.线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配.调优和监控. 9.1 线程池的实现原理 当提交一个新任务到线程池时,线程池的处理流程如下: 1)线程池判断核心线程池里的线程是否都在执行任务.如果不是,则创建

Java中的线程池

综述 在我们的开发中经常会使用到多线程.例如在Android中,由于主线程的诸多限制,像网络请求等一些耗时的操作我们必须在子线程中运行.我们往往会通过new Thread来开启一个子线程,待子线程操作完成以后通过Handler切换到主线程中运行.这么以来我们无法管理我们所创建的子线程,并且无限制的创建子线程,它们相互之间竞争,很有可能由于占用过多资源而导致死机或者OOM.所以在Java中为我们提供了线程池来管理我们所创建的线程. 线程池的使用 采用线程池的好处 在这里我们首先来说一下采用线程池的

21.线程池ThreadPoolExecutor实现原理

1. 为什么要使用线程池 在实际使用中,线程是很占用系统资源的,如果对线程管理不善很容易导致系统问题.因此,在大多数并发框架中都会使用线程池来管理线程,使用线程池管理线程主要有如下好处: 降低资源消耗.通过复用已存在的线程和降低线程关闭的次数来尽可能降低系统性能损耗: 提升系统响应速度.通过复用线程,省去创建线程的过程,因此整体上提升了系统的响应速度: 提高线程的可管理性.线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,因此,需要使用线程池来管理线程. 2. 线程池的

如何创建Java中的线程池

线程是Java的一大特性,它可以是给定的指令序列.给定的方法中定义的变量或者一些共享数据(类一级的变量).在Java中每个线程有自己的堆栈和程序计数器(PC),其中堆栈是用来跟踪线程的上下文(上下文是当线程执行到某处时,当前的局部变量的值),而程序计数器则用来跟踪当前线程正在执行的指令. 一个线程不能访问另外一个线程的堆栈变量,而且这个线程必须处于如下状态之一: 1.排队状态(Ready),在用户创建了一个线程以后,这个线程不会立即运行.当线程中的方法start()被调用时,这个线程就会进行排队

理解java中的线程池

1.引入线程池的原因 对于多线程编程,处理每个请求都要创建一个线程,这不仅要花费时间在创建线程的过程中,还会出现创建线程过多未释放导致的系统内存不足,内存溢出问题,因此引入线程池的概念.线程池,就是在一个容器中创建适量的线程,在程序访问的时候直接调用该线程即可访问. 2.类比数据库连接池. 数据库连接池与线程池类似,dao层访问数据库时,首先会,加载驱动,建立连接,而每次频繁的建立连接肯定会大大降低系统运行效率,因此,数据库连接池出现了,下面以一张图进行说明: 如上图,没连接池时访问一次数据库便

Java中的线程池ExecutorService

示例 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; public class Ch09_Executor { private static void run(ExecutorService threadPool) { for(int i = 1; i < 5; i++)

线程池ThreadPoolExecutor工作原理

前言 工作原理 如果使用过线程池,细心的同学肯定会注意到,new一个线程池,但是如果不往里面提交任何任务的话,main方法执行完之后程序会退出,但是如果向线程池中提交了任务的话,main方法执行完毕之后程序是不会自动退出的,是什么原理,或者说是什么原因导致任务提交到线程池之后任务执行完程序无法自动退出的呢?下面就让我们趴开线程池的源码,一探究竟. 我们直接从ThreadPoolExecutor的execute方法开始说起.线程提交到ThreadPoolExecutor执行分为三种情况,具体如下:

java 中使用线程池处理文件夹下面的子文件

读取某个文件夹下面的所有文件,使用多线程处理,例如读取E盘下面的文件内容: package thread; import java.io.File; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue;

Java中的线程池模拟

老规矩,直接上代码,写的不好不要喷! public class ThreadExecutrols { public static void main(String[] args) { //创建一个线程池,固定数量为3 //ExecutorService ex = Executors.newFixedThreadPool(3); //创建单一的线程 //ExecutorService ex = Executors.newSingleThreadExecutor(); //利用缓存创建默认线程,在下