Java线程池代码详解


一、线程池是什么?

在多线程编程中,线程池是一种非常重要的技术,它能够有效地管理和复用线程,提高程序的性能和响应速度。特别是在并发访问量较大的情况下,合理地利用线程池可以避免频繁地创建和销毁线程,从而减少系统开销,提高资源利用率。Java语言作为一种广泛应用于企业级开发的编程语言,其线程池技术更是备受关注。本文将深入探讨Java线程池的源码,帮助读者更好地理解线程池技术。

二、源码阅读

1.成员变量和一些基础方法

    //初始化为 11100000000000000000000000000000
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3; //29
    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; // 111
    private static final int SHUTDOWN   =  0 << COUNT_BITS; // 000
    private static final int STOP       =  1 << COUNT_BITS; // 001
    private static final int TIDYING    =  2 << COUNT_BITS; // 010
    private static final int TERMINATED =  3 << COUNT_BITS; // 011

在线程池中通过一个变量ctl的高位和低位来表示线程池的状态和线程数量。高3位表示线程池的状态,低29位表示线程池的数量。

	// 获取当前线程池的状态
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    // 获取当前线程数
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    // 创建ctl 由rs和wc 或 操作构成
    private static int ctlOf(int rs, int wc) { return rs | wc; }

还有一个关键的内部类Worker

 private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable{
        final Thread thread; //所属线程
        /** Initial task to run.  Possibly null. */
        Runnable firstTask; //执行的任务
        //构造函数
         Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
}

线程池的关键任务就是执行输入进来的任务,线程池可能会复用线程或者创建新的线程执行任务,但是任务都会被封装为Worker类进行执行。

2.构造函数

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

参数详解

  1. corePoolSize:核心线程数,即线程池中保持活动的最小线程数。当有任务提交时,线程池会首先创建这些核心线程来处理任务。
  2. maximumPoolSize:最大线程数,即线程池中允许的最大线程数量。当任务量超过核心线程数并且工作队列已满时,线程池会创建新的线程,但数量不会超过最大线程数。
  3. keepAliveTime:线程空闲时间,表示当线程池中的线程数量超过核心线程数时,多余的空闲线程等待新任务的最长时间,超过这个时间会被终止并移除。
  4. unit:时间单位,用于指定keepAliveTime的时间单位,例如TimeUnit.SECONDS表示秒
  5. workQueue:工作队列,用于存储等待执行的任务。常用的工作队列包括LinkedBlockingQueue、ArrayBlockingQueue、SynchronousQueue等。
  6. threadFactory:线程工厂,用于创建新的线程。可以使用默认的Executors.defaultThreadFactory()或自定义实现ThreadFactory接口。
  7. handler:拒绝策略,表示当工作队列已满且无法继续添加新任务时的处理策略。常见的策略包括AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy。

3.一个任务进入线程池的流程

通常我们定义好任务,使用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();
            // 如果当前线程池不处于运行状态了 移除任务
            // 一旦线程池的状态不为running了 就无法接受新的任务
            if (! isRunning(recheck) && remove(command))
            // 对于任务采取 拒绝策略
                reject(command);
            // 如果是没有线程 可以创建一个线程
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 如果加入任务队列失败 再创建非核心线程进行 处理 注意 这里addWorker传入的是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.
            //rs >= SHUTDOWN 线程池处于关闭状态 或者已经关闭
            // rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()
            // 线程处于关闭状态 且 firstTask为空 且 任务队列中存在任务 那么线程池不应该拒绝新任务 即不返回false 
            //即线程池处于shutdonw状态时 可以创建无任务线程去 执行工作队列中的任务
            //但是不能接受新的任务
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                // 线程数超过最大容量 || 线程池超过核心线程数/最大线程数 返回false
                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;  //任务是否添加到workers中
        Worker w = null;
        try {
       		//将任务封装为Worker Worker中会通过线程工厂 创建线程
            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());
                    //如果线程处运行状态 或者 处于shutdown状态态 且firstTask为空
                    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)//上述过程如果抛出了异常导致添加worker失败 
                addWorkerFailed(w); 
        }
        return workerStarted;
    }

addWorkerFailed

   private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        // 加锁
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w);
            // 减少线程计数
            decrementWorkerCount();
            tryTerminate(); //尝试关闭线程池
        } finally {
            mainLock.unlock();
        }
    }

一个任务的进入线程池的流程搞清楚了,下面可以看看任务的执行操作。
启动线程后 会调用Worker的run方法

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

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();
                //如果线程池处于stoping状态 且线程还没有被中断,就要中断线程
                // 如果线程池不处于 stopping状态 那么保证线程不处于中断状态
                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);
        }
    }

线程运行时通过while 循环保证 线程一直可以执行任务,那么如果保证线程停止和非核心线程的销毁呢?
答案就在getTask方法和processWorkerExit 中

getTask()

private Runnable getTask() {
        // 获取任务是否超时
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
               // 日常判断 线程池状态 和 任务队列是否为空
               // 其实可以看出 如果线程池处于SHUTDOWN状态下 不处理新的任务 但是对于
               // 任务队列中的任务还是去执行的。
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null; //返回为空 执行runWoker中的processWorkerExit方法
            }

            int wc = workerCountOf(c);

            // 是否可以淘汰线程
            //allowCoreThreadTimeOut 默认为false
            //如果allowCoreThreadTimeOut为true 表明所有线程都可以被淘汰
            //当线程数量大于核心线程数时表示可以淘汰线程
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
           // 如果(线程池数量大于最大线程数 或者 可以淘汰线程并且线程获取任务超时时) 
           // 而且(线程数大于1 或者 工作队列为空)
           // 减少线程计数 并且返回null 让当前线程循环结束
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                 // 是否可以超时 一般取决于 线程数是否大于核心线程数
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                
                if (r != null)
                    return r;
                // poll 获取任务超时 进入下一次循环
                timedOut = true;
            } catch (InterruptedException retry) {
            // 如果有线程中断了 poll和take都会抛出中断异常 一般会清除中断状态
                timedOut = false;
            }
        }
    }

对于线程的关闭,在线程数小于核心线程时,取决于线程池的状态,在线程数大于核心线程时,更容易因为获取任务超时而被关闭。
而且从代码中可以看出 线程处于中断状态下,可能会影响任务的获取,应该只会影响至多一次。因为一般会调用Thread.interrupted()响应中断并且清除线程的中断状态。
对于当前线程的执行,还有最后一个步骤processWorkerExit()

 private void processWorkerExit(Worker w, boolean completedAbruptly) {
 // 如果线程是突然结束循环的 不改变线程数量
        if (completedAbruptly) 
            decrementWorkerCount();
// 如果线程是因为 获取不到任务 或者 线程池的状态 执行到这个方法中
        final ReentrantLock mainLock = this.mainLock;
        // 移除当前worker
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }

        tryTerminate();//尝试关闭线程池

        int c = ctl.get();
        // 如果线程池 处于running 或者 shutdown状态
        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);
        }
    }

4.线程池如何关闭

通常关闭线程池使用shutDown方法。
shutDown()

public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        // 加锁
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 修改线程状态 无限循环 直到修改成功
            advanceRunState(SHUTDOWN);
            // 中断所有线程
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        // 尝试关闭线程池
        tryTerminate();
    }

tryTerminate()

final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            // 如果线程池处于running 或者  已经是关闭状态了
            // 或者处于shutdown 状态 并且工作队列不为空 
            // 直接return
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            // 如果线程数 不为0
            if (workerCountOf(c) != 0) { // Eligible to terminate
            // 中断一个线程 返回
                interruptIdleWorkers(ONLY_ONE);
                return;
            }
          //如果运行到这一步 说明线程池中已经没有线程了
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
            // 修改线程池状态为 tidying 
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                    //空方法 留给子类进行拓展
                        terminated();
                    } finally {
                    // 修改线程池状态为TERMINATED
                        ctl.set(ctlOf(TERMINATED, 0));
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

可以看出shutDown方法 是十分宽松的一个方法,修改线程池状态为SHUTDOWN,如果此时任务队列中有任务的话,允许线程池执行完,而且此时不允许加入新的任务到线程池中,在工作队列执行完后调用addWorker方法无法成功,无法再添加新线程了;并且正在执行的线程,执行到工作队列执行空了,后无法获取新任务了,会继续调用processWorkerExit方法,执行tryTerminate修改线程状态并且因为addWorker也无法添加新的线程,到最后所有的线程都会被销毁。

这里举两个例子
假设有两个线程A、B,此时执行了shutDown方法,假设A、B都在等新的任务,此时A被中断了,继续执行getTask方法,发现工作队列为空而且线程池状态为SHUTDOWN,循环中断,执行processWorkerExit,无法添加新的线程,而且调用tryTerminate中断一个线程B,A线程结束循环被销毁,此时B也是如上步骤被销毁。

假设有两个线程A、B,此时执行了shutDown方法,假设A、B都在执行任务队列中的方法,此时A拿到了最后一个方法,执行完毕后继续调用getTask方法,发现工作队列为空而且线程池状态为SHUTDOWN,循环中断,执行processWorkerExit,无法添加新的线程,而且调用tryTerminate中断一个线程B,A线程结束循环被销毁,此时B也是如上步骤被销毁。
综上,调用shutDown方法后,不会立即停止线程池,而是等工作队列中的任务执行完后才会销毁线程。

shutDownNow()

  public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 循环修改线程池状态为STOP
            advanceRunState(STOP);
            interruptWorkers();
            // 获取未执行的任务
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }

shutDownNow 方法会让线程池状态修改为STOP并且获取所有工作队列中的任务。即让线程池停止接受新的任务,并且不继续执行完队列中的任务,立即关闭线程池。

如何优雅的关闭线程池?
使用shutDown()和shutDownNow()配合awaitTermination(),进行线程池的优雅关闭。
awaitTermination() 等待线程关闭,可以设置等待时间。

 public static void shutdownThreadPoolGracefully(ExecutorService threadPool) {
        if (!(threadPool instanceof ExecutorService) || threadPool.isTerminated()) {
            return;
        }
        try {
            threadPool.shutdown();   //拒绝接受新任务
        } catch (SecurityException e) {
            return;
        } catch (NullPointerException e) {
            return;
        }
        try {
            // 等待 60 s,等待线程池中的任务完成执行
            if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
                // 调用 shutdownNow 取消正在执行的任务
                threadPool.shutdownNow();
                // 再次等待 60 s,如果还未结束,可以再次尝试,或则直接放弃
                if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
                    System.err.println("线程池任务未正常执行结束");
                }
            }
        } catch (InterruptedException ie) {
            // 捕获异常,重新调用 shutdownNow
            threadPool.shutdownNow();
        }
        //任然没有关闭,循环关闭10次,每次等待10毫秒
        if (!threadPool.isTerminated()) {
            try {
                for (int i = 0; i < 10; i++) {
                    if (threadPool.awaitTermination(10, TimeUnit.MILLISECONDS)) {
                        break;
                    }
                    threadPool.shutdownNow();
                }
            } catch (InterruptedException e) {
                System.err.println(e.getMessage());
            } catch (Throwable e) {
                System.err.println(e.getMessage());
            }
        }
    }

线程池会在什么时候加锁

添加和删除worker时会加锁
添加worker失败时会加锁
尝试关闭线程池时会加锁
检查线程状态时会加锁
线程池中断线程时会加锁	
调用等待线程池关方法时会加锁
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值