ThreadPoolExecutor worker为何要实现AQS

目录

简单介绍

基本特征

实现AQS的原因

设计的期望

应用的场景

2.1关闭线程池 

2.2调整核心线程数和最大线程数

2.3调整线程空闲时间


简单介绍

引用jdk1.8对于Worker类的官方注释

/**
 * Class Worker mainly maintains interrupt control state for
 * threads running tasks, along with other minor bookkeeping.
 * This class opportunistically extends AbstractQueuedSynchronizer
 * to simplify acquiring and releasing a lock surrounding each
 * task execution.  This protects against interrupts that are
 * intended to wake up a worker thread waiting for a task from
 * instead interrupting a task being run.  We implement a simple
 * non-reentrant mutual exclusion lock rather than use
 * ReentrantLock because we do not want worker tasks to be able to
 * reacquire the lock when they invoke pool control methods like
 * setCorePoolSize.  Additionally, to suppress interrupts until
 * the thread actually starts running tasks, we initialize lock
 * state to a negative value, and clear it upon start (in
 * runWorker).
 */

基本特征

1.Worker执行任务时获得锁,执行完毕释放锁。

2.Worker具有不可重入特性,目的是为了防止worker在线程池控制类操作时获得锁。

实现AQS的原因

设计的期望

设计者对worker的期望,在空闲时可以响应中断,在执行任务时不可被中断

应用的场景

实现worker设计目标的方式,就是在中断之前首先尝试获得锁,trylock,由于不可重入特性,执行任务中的worker已经获得了lock,trylock失败,无法再中断woker对象的thread。

下面列举一下对应的场景

2.1关闭线程池 

关闭线程池时,要优雅的关闭,不能粗暴的将所有的任务直接停掉

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(SHUTDOWN);
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

2.2调整核心线程数和最大线程数

调整核心线程数和最大线程数,可能会需要关闭某些worker,比如,当前线程数大于了核心线程数或者最大线程数,需要将多余的线程停掉,但停掉时还是要优雅,不能很粗暴

public void setCorePoolSize(int corePoolSize) {
    if (corePoolSize < 0)
        throw new IllegalArgumentException();
    int delta = corePoolSize - this.corePoolSize;
    this.corePoolSize = corePoolSize;
    if (workerCountOf(ctl.get()) > corePoolSize)
        interruptIdleWorkers();
    else if (delta > 0) {
        // We don't really know how many new threads are "needed".
        // As a heuristic, prestart enough new workers (up to new
        // core size) to handle the current number of tasks in
        // queue, but stop if queue becomes empty while doing so.
        int k = Math.min(delta, workQueue.size());
        while (k-- > 0 && addWorker(null, true)) {
            if (workQueue.isEmpty())
                break;
        }
    }
}
public void setMaximumPoolSize(int maximumPoolSize) {
    if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize)
        throw new IllegalArgumentException();
    this.maximumPoolSize = maximumPoolSize;
    if (workerCountOf(ctl.get()) > maximumPoolSize)
        interruptIdleWorkers();
}

2.3调整线程空闲时间

这个与前面的原因类似

public void allowCoreThreadTimeOut(boolean value) {
    if (value && keepAliveTime <= 0)
        throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
    if (value != allowCoreThreadTimeOut) {
        allowCoreThreadTimeOut = value;
        if (value)
            interruptIdleWorkers();
    }
}

再这些场景中,实现的核心是interruptIdleWorkers函数,下面是这个函数的源码。注意红色部分的两个条件,一是没有被中断的,二是要获得worker的锁,只有空闲的线程才能获得到锁,工作的中的线程是无法获得再次获得锁的,这也对应了worker类的关于不可重入的注释

private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers) {
            Thread t = w.thread;
            if (!t.isInterrupted() && w.tryLock()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                } finally {
                    w.unlock();
                }
            }
            if (onlyOne)
                break;
        }
    } finally {
        mainLock.unlock();
    }
}

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);
        .....

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ThreadPoolExecutor是Java中的一个线程池实现,可以帮助我们管理线程池中的线程,从而优化程序的性能。下面是一个简单的ThreadPoolExecutor实现示例: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public class ThreadPoolExecutorDemo { public static void main(String[] args) { // 创建一个线程池,其中包含5个线程 ExecutorService executor = Executors.newFixedThreadPool(5); // 将任务提交到线程池中 for (int i = 0; i < 10; i++) { Runnable worker = new WorkerThread("Task " + i); executor.execute(worker); } // 关闭线程池 executor.shutdown(); while (!executor.isTerminated()) { } System.out.println("Finished all threads"); } } class WorkerThread implements Runnable { private String taskName; public WorkerThread(String taskName) { this.taskName = taskName; } @Override public void run() { System.out.println(Thread.currentThread().getName() + " executing " + taskName); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } ``` 在这个示例中,我们使用了Executors工厂类创建了一个ThreadPoolExecutor,该线程池包含5个线程。我们使用一个for循环将10个任务提交到线程池中,并且在每个任务执行时都会输出线程名和任务名。最后,我们调用shutdown()方法关闭线程池,并且通过isTerminated()方法判断线程池中的所有线程是否都已经执行完毕。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值