网络安全最全线程池从设计思想到源码解读_线程池设计的基本思想,2024年最新BTAJ面试有关散列(哈希)表的面试题详解

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

new LinkedBlockingQueue(),
                                  threadFactory);
}


使用示例:



// 1. 创建线程池对象,设置核心线程和最大线程数为5
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
// 2. 创建Runnable(任务)
Runnable task =new Runnable(){
  public void run() {
     System.out.println(Thread.currentThread().getName() + “—>运行”);
  }
};
// 3. 向线程池提交任务
fixedThreadPool.execute(task);


**「2、 SingleThreadExecutor」**


单线程线程池。特点是线程池中只有一个线程(核心线程),线程执行完任务立即回收,使用有界阻塞队列(容量未指定,使用默认值`Integer.MAX_VALUE`)



public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue()));
}
// 为节省篇幅,省略了自定义线程工厂方式的源码


使用示例:



// 1. 创建单线程线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 2. 创建Runnable(任务)
Runnable task = new Runnable(){
  public void run() {
     System.out.println(Thread.currentThread().getName() + “—>运行”);
  }
};
// 3. 向线程池提交任务
singleThreadExecutor.execute(task);


**「3、 ScheduledThreadPool」**


定时线程池。指定核心线程数量,普通线程数量无限,线程执行完任务立即回收,任务队列为延时阻塞队列。这是一个比较特别的线程池,适用于**「执行定时或周期性的任务」**。



public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

// 继承了 ThreadPoolExecutor
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor
        implements ScheduledExecutorService {
    // 构造函数,省略了自定义线程工厂的构造函数
 public ScheduledThreadPoolExecutor(int corePoolSize) {
     super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
           new DelayedWorkQueue());
 }
 
 // 延时执行任务
 public ScheduledFuture<?> schedule(Runnable command,                                        long delay,                                        TimeUnit unit) {         ...     }  // 定时执行任务  public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {…}
}


使用示例:



// 1. 创建定时线程池
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
// 2. 创建Runnable(任务)
Runnable task = new Runnable(){
  public void run() {
     System.out.println(Thread.currentThread().getName() + “—>运行”);
  }
};
// 3. 向线程池提交任务
scheduledThreadPool.schedule(task, 2, TimeUnit.SECONDS); // 延迟2s后执行任务
scheduledThreadPool.scheduleAtFixedRate(task,50,2000,TimeUnit.MILLISECONDS);// 延迟50ms后、每隔2000ms执行任务


**「4、CachedThreadPool」**


缓存线程池。没有核心线程,普通线程数量为`Integer.MAX_VALUE`(可以理解为无限),线程闲置60s后回收,任务队列使用`SynchronousQueue`这种无容量的同步队列。适用于**「任务量大但耗时低」**的场景。



public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue());
}


使用示例:



// 1. 创建缓存线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 2. 创建Runnable(任务)
Runnable task = new Runnable(){
  public void run() {
     System.out.println(Thread.currentThread().getName() + “—>运行”);
  }
};
// 3. 向线程池提交任务
cachedThreadPool.execute(task);


### 解读线程池


OK,相信前面内容阅读起来还算轻松愉悦吧,那么从这里开始就进入深水区了,如果后面内容能吃透,那么线程池知识就真的被你掌握了。


我们知道,向线程池提交任务是用`ThreadPoolExecutor`的`execute()`方法,但在其内部,线程任务的处理其实是相当复杂的,涉及到`ThreadPoolExecutor`、`Worker`、`Thread`三个类的6个方法:


![图片](https://img-blog.csdnimg.cn/img_convert/f9951fa25a74ce14b4b03d1a20b8f3a7.png)


向线程池提交任务


### execute()


在`ThreadPoolExecutor`类中,任务提交方法的入口是`execute(Runnable command)`方法(`submit()`方法也是调用了`execute()`),该方法其实只在尝试做一件事:经过各种校验之后,调用 `addWorker(Runnable command,boolean core)`方法为线程池创建一个线程并执行任务,与之相对应,execute() 的结果有两个:


**「参数说明:」**


1. **「Runnable command」**:待执行的任务


**「执行流程:」**


1、通过 `ctl.get()` 得到线程池的当前线程数,如果线程数小于`corePoolSize`,则调用 `addWorker(commond,true)`方法创建新的线程执行任务,否则执行步骤2;


2、步骤1失败,说明已经无法再创建新线程,那么考虑将任务放入阻塞队列,等待执行完任务的线程来处理。基于此,判断线程池是否处于`Running`状态(只有`Running`状态的线程池可以接受新任务),如果任务添加到任务队列成功则进入步骤3,失败则进入步骤4;


3、来到这一步需要说明任务已经加入任务队列,这时要二次校验线程池的状态,会有以下情形:


* 线程池不再是`Running`状态了,需要将任务从任务队列中移除,如果移除成功则拒绝本次任务
* 线程池是`Running`状态,则判断线程池工作线程是否为0,是则调用 `addWorker(commond,true)`添加一个没有初始任务的线程(这个线程将去获取已经加入任务队列的本次任务并执行),否则进入步骤4;
* 线程池不是`Running`状态,但从任务队列移除任务失败(可能已被某线程获取?),进入步骤4;


4、将线程池扩容至`maximumPoolSize`并调用 `addWorker(commond,false)`方法创建新的线程执行任务,失败则拒绝本次任务。


**「流程图:」**


![图片](https://img-blog.csdnimg.cn/img_convert/bd36bd3bb47774943bbb1920017f8870.png)


创建新的线程执行任务


**「源码详读:」**



/**
 * 在将来的某个时候执行给定的任务。任务可以在新线程中执行,也可以在现有的池线程中执行。
 * 如果由于此执行器已关闭或已达到其容量而无法提交任务以供执行,则由当前的{@code RejectedExecutionHandler}处理该任务。
 * 
 * @param command the task to execute  待执行的任务命令
 /
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /

     * Proceed in 3 steps:
     * 
     * 1. 如果运行的线程少于corePoolSize,将尝试以给定的命令作为第一个任务启动新线程。
     *
     * 2. 如果一个任务可以成功排队,那么我们仍然需要仔细检查两点,其一,我们是否应该添加一个线程
     * (因为自从上次检查至今,一些存在的线程已经死亡),其二,线程池状态此时已改变成非运行态。因此,我们重新检查状态,如果检查不通过,则移除已经入列的任务,如果检查通过且线程池线程数为0,则启动新线程。
     * 
     * 3. 如果无法将任务加入任务队列,则将线程池扩容到极限容量并尝试创建一个新线程,如果失败则拒绝任务。
     */
    int c = ctl.get();
   
    // 步骤1:判断线程池当前线程数是否小于线程池大小
    if (workerCountOf© < corePoolSize) {
        // 增加一个工作线程并添加任务,成功则返回,否则进行步骤2
        // true代表使用coreSize作为边界约束,否则使用maximumPoolSize
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 步骤2:不满足workerCountOf© < corePoolSize或addWorker失败,进入步骤2
    // 校验线程池是否是Running状态且任务是否成功放入workQueue(阻塞队列)
    if (isRunning© && workQueue.offer(command)) {
        int recheck = ctl.get();
        // 再次校验,如果线程池非Running且从任务队列中移除任务成功,则拒绝该任务
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 如果线程池工作线程数量为0,则新建一个空任务的线程
        else if (workerCountOf(recheck) == 0)
            // 如果线程池不是Running状态,是加入不进去的
            addWorker(null, false);
    }
    // 步骤3:如果线程池不是Running状态或任务入列失败,尝试扩容maxPoolSize后再次addWorker,失败则拒绝任务
    else if (!addWorker(command, false))
        reject(command);
}


### addWorker()


`addWorker(Runnable firstTask, boolean core)` 方法,顾名思义,向线程池添加一个带有任务的工作线程。


**「参数说明:」**


1. **「Runnable firstTask」**:新创建的线程应该首先运行的任务(如果没有,则为空)。
2. **「boolean core」**:该参数决定了线程池容量的约束条件,即当前线程数量以何值为极限值。参数为 `true` 则使用`corePollSize` 作为约束值,否则使用`maximumPoolSize`。


**「执行流程:」**


1、外层循环判断线程池的状态是否可以新增工作线程。这层校验基于下面两个原则:


* 线程池为`Running`状态时,既可以接受新任务也可以处理任务
* 线程池为关闭状态时只能新增空任务的工作线程(`worker`)处理任务队列(`workQueue`)中的任务不能接受新任务


2、内层循环向线程池添加工作线程并返回是否添加成功的结果。


* 首先校验线程数是否已经超限制,是则返回`false`,否则进入下一步
* 通过`CAS`使工作线程数+1,成功则进入步骤3,失败则再次校验线程池是否是运行状态,是则继续内层循环,不是则返回外层循环


3、核心线程数量+1成功的后续操作:添加到工作线程集合,并启动工作线程


* 首先获取锁之后,再次校验线程池状态(具体校验规则见代码注解),通过则进入下一步,未通过则添加线程失败
* 线程池状态校验通过后,再检查线程是否已经启动,是则抛出异常,否则尝试将线程加入线程池
* 检查线程是否启动成功,成功则返回`true`,失败则进入 `addWorkerFailed` 方法


**「流程图:」**


![图片](https://img-blog.csdnimg.cn/img_convert/f3be782d306de51860e44c7d57548e7a.png)


向线程池添加一个带有任务的工作线程


**「源码详读:」**



private boolean addWorker(Runnable firstTask, boolean core) {
    // 外层循环:判断线程池状态
    retry:
    for (;😉 {
        int c = ctl.get();
        int rs = runStateOf©;

/** 
         * 1.线程池为非Running状态(Running状态则既可以新增核心线程也可以接受任务)
         * 2.线程为shutdown状态且firstTask为空且队列不为空
         * 3.满足条件1且条件2不满足,则返回false
         * 4.条件2解读:线程池为shutdown状态时且任务队列不为空时,可以新增空任务的线程来处理队列中的任务
         */
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

// 内层循环:线程池添加核心线程并返回是否添加成功的结果
        for (;😉 {
            int wc = workerCountOf©;
            // 校验线程池已有线程数量是否超限:
            // 1.线程池最大上限CAPACITY 
            // 2.corePoolSize或maximumPoolSize(取决于入参core)
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize)) 
                return false;
            // 通过CAS操作使工作线程数+1,跳出外层循环
            if (compareAndIncrementWorkerCount©) 
                break retry;
            // 线程+1失败,重读ctl
            c = ctl.get();   // Re-read ctl
            // 如果此时线程池状态不再是running,则重新进行外层循环
            if (runStateOf© != rs)
                continue retry;
            // 其他 CAS 失败是因为工作线程数量改变了,继续内层循环尝试CAS对线程数+1
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

/**
     * 核心线程数量+1成功的后续操作:添加到工作线程集合,并启动工作线程
     */
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        final ReentrantLock mainLock = this.mainLock;
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            // 下面代码需要加锁:线程池主锁
            mainLock.lock(); 
            try {
                // 持锁期间重新检查,线程工厂创建线程失败或获取锁之前关闭的情况发生时,退出
                int c = ctl.get();
                int rs = runStateOf©;

// 再次检验线程池是否是running状态或线程池shutdown但线程任务为空
                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 {
        //线程启动失败,则进入addWorkerFailed
        if (! workerStarted) 
            addWorkerFailed(w);
    }
    return workerStarted;
}


### Worker类


`Worker`类是内部类,既实现了`Runnable`,又继承了`AbstractQueuedSynchronizer`(以下简称`AQS`),所以其既是一个可执行的任务,又可以达到锁的效果。


`Worker`类主要维护正在运行任务的线程的中断控制状态,以及其他次要的记录。这个类适时地继承了`AbstractQueuedSynchronizer`类,以简化获取和释放锁(该锁作用于每个任务执行代码)的过程。这样可以防止去中断正在运行中的任务,只会中断在等待从任务队列中获取任务的线程。


我们实现了一个简单的不可重入互斥锁,而不是使用可重入锁,因为我们不希望工作任务在调用`setCorePoolSize`之类的池控制方法时能够重新获取锁。另外,为了在线程真正开始运行任务之前禁止中断,我们将锁状态初始化为负值,并在启动时清除它(在`runWorker`中)。



private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    /**
     * This class will never be serialized, but we provide a
     * serialVersionUID to suppress a javac warning.
     /
    private static final long serialVersionUID = 6138294804551838833L;
 
    /
* Thread this worker is running in.  Null if factory fails. /
    final Thread thread; 
     
    /
* Initial task to run.  Possibly null. /
    Runnable firstTask;
     
    /
* Per-thread task counter /
    volatile long completedTasks;
 
    /
*
     * Creates with given first task and thread from ThreadFactory.
     * @param firstTask the first task (null if none)
     /
    // 通过构造函数初始化,
    Worker(Runnable firstTask) {
        //设置AQS的同步状态
        // state:锁状态,-1为初始值,0为unlock状态,1为lock状态
        setState(-1); // inhibit interrupts until runWorker  在调用runWorker前,禁止中断
       
        this.firstTask = firstTask;
        // 线程工厂创建一个线程
        this.thread = getThreadFactory().newThread(this); 
    }
 
    /
* Delegates main run loop to outer runWorker  /
    public void run() {
        runWorker(this); //runWorker()是ThreadPoolExecutor的方法
    }
 
    // Lock methods
    // The value 0 represents the unlocked state. 0代表“没被锁定”状态
    // The value 1 represents the locked state. 1代表“锁定”状态
 
    protected boolean isHeldExclusively() {
        return getState() != 0;
    }
 
    /
*
     * 尝试获取锁的方法
     * 重写AQS的tryAcquire(),AQS本来就是让子类来实现的
     /
    protected boolean tryAcquire(int unused) {
        // 判断原值为0,且重置为1,所以state为-1时,锁无法获取。
        // 每次都是0->1,保证了锁的不可重入性
        if (compareAndSetState(0, 1)) {
            // 设置exclusiveOwnerThread=当前线程
            setExclusiveOwnerThread(Thread.currentThread()); 
            return true;
        }
        return false;
    }
 
    /
*
     * 尝试释放锁
     * 不是state-1,而是置为0
     /
    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null); 
        setState(0);
        return true;
    }
 
    public void lock()        { acquire(1); }
    public boolean tryLock()  { return tryAcquire(1); }
    public void unlock()      { release(1); }
    public boolean isLocked() { return isHeldExclusively(); }
 
    /
*
     * 中断(如果运行)
     * shutdownNow时会循环对worker线程执行
     * 且不需要获取worker锁,即使在worker运行时也可以中断
     */
    void interruptIfStarted() {
        Thread t;
        //如果state>=0、t!=null、且t没有被中断
        //new Worker()时state==-1,说明不能中断
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}


### runWorker()


可以说,`runWorker(Worker w)` 是线程池中真正处理任务的方法,前面的`execute()` 和 `addWorker()` 都是在为该方法做准备和铺垫。


**「参数说明:」**


1. **「Worker w」**:封装的Worker,携带了工作线程的诸多要素,包括`Runnable`(待处理任务)、`lock`(锁)、`completedTasks`(记录线程池已完成任务数)


**「执行流程:」**


1、判断当前任务或者从任务队列中获取的任务是否不为空,都为空则进入步骤2,否则进入步骤3


2、任务为空,则将`completedAbruptly`置为`false`(即线程不是突然终止),并执行`processWorkerExit(w,completedAbruptly)`方法进入线程退出程序


3、任务不为空,则进入循环,并加锁


4、判断是否为线程添加中断标识,以下两个条件满足其一则添加中断标识:


* 线程池状态>=`STOP`,即`STOP`或`TERMINATED`
* 一开始判断线程池状态<`STOP`,接下来检查发现`Thread.interrupted()`为`true`,即线程已经被中断,再次检查线程池状态是否>=`STOP`(以消除该瞬间`shutdown`方法生效,使线程池处于`STOP`或`TERMINATED`)


5、执行前置方法 `beforeExecute(wt, task)`(该方法为空方法,由子类实现)后执行`task.run()` 方法执行任务(执行不成功抛出相应异常)


6、执行后置方法 `afterExecute(task, thrown)`(该方法为空方法,由子类实现)后将线程池已完成的任务数+1,并释放锁。


7、再次进行循环条件判断。


**「流程图:」**


![图片](https://img-blog.csdnimg.cn/img_convert/a4a2e4d81bca6b6922e1247f347e0fc1.png)


线程池流程图


**「源码详读:」**



final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // allow interrupts
    // new Worker()是state==-1,此处是调用Worker类的tryRelease()方法,将state置为0,而interruptIfStarted()中只有state>=0才允许调用中断
    w.unlock(); 
            
    // 线程退出的原因,true是任务导致,false是线程正常退出
    boolean completedAbruptly = true; 
    try {
        // 当前任务和从任务队列中获取的任务都为空,方停止循环
        while (task != null || (task = getTask()) != null) {
            //上锁可以防止在shutdown()时终止正在运行的worker,而不是应对并发
            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
            /**
             * 判断1:确保只有在线程处于stop状态且wt未中断时,wt才会被设置中断标识
             * 条件1:线程池状态>=STOP,即STOP或TERMINATED
             * 条件2:一开始判断线程池状态<STOP,接下来检查发现Thread.interrupted()为true,即线程已经被中断,再次检查线程池状态是否>=STOP(以消除该瞬间shutdown方法生效,使线程池处于STOP或TERMINATED),
             * 条件1与条件2任意满意一个,且wt不是中断状态,则中断wt,否则进入下一步
             */
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt(); //当前线程调用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++; //完成任务数+1
                w.unlock(); //释放锁
            }
        }
        // 
        completedAbruptly = false;
    } 
    finally {
        //处理worker的退出
        processWorkerExit(w, completedAbruptly);
    }
}


**「5、getTask()」**


由函数调用关系图可知,在`ThreadPoolExecutor`类的实现中,`Runnable getTask()` 方法是为`void runWorker(Worker w)`方法服务的,它的作用就是在任务队列(`workQueue`)中获取 task(`Runnable`)。


**「参数说明」**:无参数


**「执行流程」**:


1. 将`timedOut`(上次获取任务是否超时)置为`false`(首次执行方法,无上次,自然为`false`),进入一个无限循环
2. 如果线程池为`Shutdown`状态且任务队列为空(线程池`shutdown`状态可以处理任务队列中的任务,不再接受新任务,这个是重点)或者线程池为`STOP`或`TERMINATED`状态,则意味着线程池不必再获取任务了,当前工作线程数量-1并返回`null`,否则进入步骤3
3. 如果线程池数量超限制或者时间超限且(任务队列为空或当前线程数>1),则进入步骤4,否则进入步骤5。
4. 移除工作线程,成功则返回`null`,不成功则进入下轮循环。
5. 尝试用`poll()` 或者 `take()`(具体用哪个取决于`timed`的值)获取任务,如果任务不为空,则返回该任务。如果为空,则将`timeOut` 置为 `true`进入下一轮循环。如果获取任务过程发生异常,则将 `timeOut`置为 false 后进入下一轮循环。


**「流程图」**:


![图片](https://img-blog.csdnimg.cn/img_convert/98857b98c632fbd22a8d9807c4616008.png)


线程池流程图


**「源码详读:」**



private Runnable getTask() {
    // 最新一次poll是否超时
    boolean timedOut = false; // Did the last poll() time out?

for (;😉 {
        int c = ctl.get();
        int rs = runStateOf©;

// Check if queue empty only if necessary.
        /**
         * 条件1:线程池状态SHUTDOWN、STOP、TERMINATED状态
         * 条件2:线程池STOP、TERMINATED状态或workQueue为空
         * 条件1与条件2同时为true,则workerCount-1,并且返回null
         * 注:条件2是考虑到SHUTDOWN状态的线程池不会接受任务,但仍会处理任务
         */
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

int wc = workerCountOf©;

// Are workers subject to culling?
        /**
         * 下列两个条件满足任意一个,则给当前正在尝试获取任务的工作线程设置阻塞时间限制(超时会被销毁?不太确定这点),否则线程可以一直保持活跃状态
         * 1.allowCoreThreadTimeOut:当前线程是否以keepAliveTime为超时时限等待任务
         * 2.当前线程数量已经超越了核心线程数
         */
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            
        // 两个条件全部为true,则通过CAS使工作线程数-1,即剔除工作线程
        // 条件1:工作线程数大于maximumPoolSize,或(工作线程阻塞时间受限且上次在任务队列拉取任务超时)
        // 条件2:wc > 1或任务队列为空
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            // 移除工作线程,成功则返回null,不成功则进入下轮循环
            if (compareAndDecrementWorkerCount©)
                return null;
            continue;
        }

// 执行到这里,说明已经经过前面重重校验,开始真正获取task了
        try {
            // 如果工作线程阻塞时间受限,则使用poll(),否则使用take()
            // poll()设定阻塞时间,而take()无时间限制,直到拿到结果为止
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            // r不为空,则返回该Runnable
            if (r != null)
                return r;
            // 没能获取到Runable,则将最近获取任务是否超时设置为true
            timedOut = true;
        } catch (InterruptedException retry) {
            // 响应中断,进入下一次循环前将最近获取任务超时状态置为false
            timedOut = false;
        }
    }
}


### processWorkerExit()


`processWorkerExit(Worker w, boolean completedAbruptly)`执行线程退出的方法


**「参数说明:」**


1. **「Worker w」**:要结束的工作线程。
2. **「boolean completedAbruptly」**:是否突然完成(异常导致),如果工作线程因为用户异常死亡,则`completedAbruptly`参数为 `true`。




还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!


王岚嵚工程师面试题(附答案),只能帮兄弟们到这儿了!如果你能答对70%,找一个安全工作,问题不大。


对于有1-3年工作经验,想要跳槽的朋友来说,也是很好的温习资料!


【完整版领取方式在文末!!】


***93道网络安全面试题***


![](https://img-blog.csdnimg.cn/img_convert/6679c89ccd849f9504c48bb02882ef8d.png)








![](https://img-blog.csdnimg.cn/img_convert/07ce1a919614bde78921fb2f8ddf0c2f.png)





![](https://img-blog.csdnimg.cn/img_convert/44238619c3ba2d672b5b8dc4a529b01d.png)





内容实在太多,不一一截图了


### 黑客学习资源推荐


最后给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!


对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

#### 1️⃣零基础入门


##### ① 学习路线


对于从来没有接触过网络安全的同学,我们帮你准备了详细的**学习成长路线图**。可以说是**最科学最系统的学习路线**,大家跟着这个大的方向学习准没问题。


![image](https://img-blog.csdnimg.cn/img_convert/acb3c4714e29498573a58a3c79c775da.gif#pic_center)


##### ② 路线对应学习视频


同时每个成长路线对应的板块都有配套的视频提供:


![image-20231025112050764](https://img-blog.csdnimg.cn/874ad4fd3dbe4f6bb3bff17885655014.png#pic_center)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值