2024年最全线程池从设计思想到源码解读_线程池设计的基本思想(2),附学习笔记+面试整理+进阶书籍

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

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

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

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

93道网络安全面试题

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

黑客学习资源推荐

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

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

1️⃣零基础入门
① 学习路线

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

image

② 路线对应学习视频

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

image-20231025112050764

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

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

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

  • 「unit」(必需):keepAliveTime参数的时间单位。有:TimeUnit.DAYS(天)、TimeUnit.HOURS(小时)、TimeUnit.MINUTES(分钟)、「TimeUnit.SECONDS(秒)」「TimeUnit.MILLISECONDS(毫秒)」、TimeUnit.MICROSECONDS(微秒)、TimeUnit.NANOSECONDS(纳秒)
  • 「workQueue」(必需):任务队列,采用阻塞队列实现。当核心线程全部繁忙时,后续由execute方法提交的Runnable将存放在任务队列中,等待被线程处理。
  • 「threadFactory」(可选):线程工厂。指定线程池创建线程的方式。
  • 「handler」(可选):拒绝策略。当线程池中线程数达到maximumPoolSizeworkQueue打满时,后续提交的任务将被拒绝,handler可以指定用什么方式拒绝任务。

放到一起再看一下:

图片

工厂与线程池

任务队列

使用ThreadPoolExecutor需要指定一个实现了BlockingQueue接口的任务等待队列。在ThreadPoolExecutor线程池的API文档中,一共推荐了三种等待队列,它们是:SynchronousQueueLinkedBlockingQueueArrayBlockingQueue

  1. 「SynchronousQueue」:同步队列。这是一个内部没有任何容量的阻塞队列,任何一次插入操作的元素都要等待相对的删除/读取操作,否则进行插入操作的线程就要一直等待,反之亦然。
  2. 「LinkedBlockingQueue」:无界队列(严格来说并非无界,上限是Integer.MAX_VALUE),基于链表结构。使用无界队列后,当核心线程都繁忙时,后续任务可以无限加入队列,因此线程池中线程数不会超过核心线程数。这种队列可以提高线程池吞吐量,但代价是牺牲内存空间,甚至会导致内存溢出。另外,使用它时可以指定容量,这样它也就是一种有界队列了。
  3. 「ArrayBlockingQueue」:有界队列,基于数组实现。在线程池初始化时,指定队列的容量,后续无法再调整。这种有界队列有利于防止资源耗尽,但可能更难调整和控制。

另外,Java还提供了另外4种队列:

  1. 「PriorityBlockingQueue」:支持优先级排序的无界阻塞队列。存放在PriorityBlockingQueue中的元素必须实现Comparable接口,这样才能通过实现compareTo()方法进行排序。优先级最高的元素将始终排在队列的头部;PriorityBlockingQueue不会保证优先级一样的元素的排序,也不保证当前队列中除了优先级最高的元素以外的元素,随时处于正确排序的位置。
  2. 「DelayQueue」:延迟队列。基于二叉堆实现,同时具备:无界队列、阻塞队列、优先队列的特征。DelayQueue延迟队列中存放的对象,必须是实现Delayed接口的类对象。通过执行时延从队列中提取任务,时间没到任务取不出来。更多内容请见DelayQueue:面试官:谈谈Java中的阻塞延迟队列DelayQueue原理和用法
  3. 「LinkedBlockingDeque」:双端队列。基于链表实现,既可以从尾部插入/取出元素,还可以从头部插入元素/取出元素。
  4. 「LinkedTransferQueue」:由链表结构组成的无界阻塞队列。这个队列比较特别的时,采用一种预占模式,意思就是消费者线程取元素时,如果队列不为空,则直接取走数据,若队列为空,那就生成一个节点(节点元素为null)入队,然后消费者线程被等待在这个节点上,后面生产者线程入队时发现有一个元素为null的节点,生产者线程就不入队了,直接就将元素填充到该节点,并唤醒该节点等待的线程,被唤醒的消费者线程取走元素。

拒绝策略

线程池有一个重要的机制:拒绝策略。当线程池workQueue已满且无法再创建新线程池时,就要拒绝后续任务了。拒绝策略需要实现RejectedExecutionHandler接口,不过Executors框架已经为我们实现了4种拒绝策略:

  1. 「AbortPolicy」(默认):丢弃任务并抛出RejectedExecutionException异常。
  2. 「CallerRunsPolicy」:直接运行这个任务的run方法,但并非是由线程池的线程处理,而是交由任务的调用线程处理。
  3. 「DiscardPolicy」:直接丢弃任务,不抛出任何异常。
  4. 「DiscardOldestPolicy」:将当前处于等待队列列头的等待任务强行取出,然后再试图将当前被拒绝的任务提交到线程池执行。

线程工厂指定创建线程的方式,这个参数不是必选项,Executors类已经为我们非常贴心地提供了一个默认的线程工厂:

/**
 * The default thread factory
 */
static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                              Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                      poolNumber.getAndIncrement() +
                     "-thread-";
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                              namePrefix + threadNumber.getAndIncrement(),
                              0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

线程池状态

线程池有5种状态:

volatile int runState;
// 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;

runState表示当前线程池的状态,它是一个 volatile 变量用来保证线程之间的可见性。

下面的几个static final变量表示runState可能的几个取值,有以下几个状态:

  • 「RUNNING」:当创建线程池后,初始时,线程池处于RUNNING状态;
  • 「SHUTDOWN」:如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
  • 「STOP」:如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
  • 「TERMINATED」:当线程池处于SHUTDOWNSTOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。

初始化&容量调整&关闭

「1、线程初始化」

默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。

在实际中如果需要线程池创建之后立即创建线程,可以通过以下两个方法办到:

  • 「prestartCoreThread()」:boolean prestartCoreThread(),初始化一个核心线程
  • 「prestartAllCoreThreads()」:int prestartAllCoreThreads(),初始化所有核心线程,并返回初始化的线程数
public boolean prestartCoreThread() {
    return addIfUnderCorePoolSize(null); //注意传进去的参数是null
}

public int prestartAllCoreThreads() {
    int n = 0;
    while (addIfUnderCorePoolSize(null))//注意传进去的参数是null
        ++n;
    return n;
}

「2、线程池关闭」

ThreadPoolExecutor提供了两个方法,用于线程池的关闭:

  • 「shutdown()」:不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
  • 「shutdownNow()」:立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

「3、线程池容量调整」

ThreadPoolExecutor提供了动态调整线程池容量大小的方法:

  • 「setCorePoolSize」:设置核心池大小
  • 「setMaximumPoolSize」:设置线程池最大能创建的线程数目大小

当上述参数从小变大时,ThreadPoolExecutor进行线程赋值,还可能立即创建新的线程来执行任务。

使用线程池

ThreadPoolExecutor

通过构造方法使用ThreadPoolExecutor是线程池最直接的使用方式,下面看一个实例:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyTest {
 public static void main(String[] args) {
  // 创建线程池
  ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 5, 5, TimeUnit.SECONDS,
    new ArrayBlockingQueue<Runnable>(5));
  // 向线程池提交任务
  for (int i = 0; i < threadPool.getCorePoolSize(); i++) {
   threadPool.execute(new Runnable() {
    @Override
    public void run() {
     for (int x = 0; x < 2; x++) {
      System.out.println(Thread.currentThread().getName() + ":" + x);
      try {
       Thread.sleep(2000);
      } catch (InterruptedException e) {
       e.printStackTrace();
      }
     }
    }
   });
  }

  // 关闭线程池
  threadPool.shutdown(); // 设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
  // threadPool.shutdownNow(); // 设置线程池的状态为STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,该方法要慎用,容易造成不可控的后果
 }
}

运行结果:

pool-1-thread-2:0
pool-1-thread-1:0
pool-1-thread-3:0
pool-1-thread-2:1
pool-1-thread-3:1
pool-1-thread-1:1

Executors封装线程池

另外,Executors封装好了4种常见的功能线程池(还是那么地贴心):

「1、FixedThreadPool」

固定容量线程池。其特点是最大线程数就是核心线程数,意味着线程池只能创建核心线程,keepAliveTime为0,即线程执行完任务立即回收。任务队列未指定容量,代表使用默认值Integer.MAX_VALUE。适用于需要控制并发线程的场景。

// 使用默认线程工厂
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
// 需要自定义线程工厂
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>(),
                                  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<Runnable>()));
}
// 为节省篇幅,省略了自定义线程工厂方式的源码

使用示例:

// 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<Runnable>());
}

使用示例:

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

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

图片

向线程池提交任务

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)方法创建新的线程执行任务,失败则拒绝本次任务。

「流程图:」

图片

创建新的线程执行任务

「源码详读:」

/**
 * 在将来的某个时候执行给定的任务。任务可以在新线程中执行,也可以在现有的池线程中执行。
 * 如果由于此执行器已关闭或已达到其容量而无法提交任务以供执行,则由当前的{@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(c) < corePoolSize) {
        // 增加一个工作线程并添加任务,成功则返回,否则进行步骤2
        // true代表使用coreSize作为边界约束,否则使用maximumPoolSize
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 步骤2:不满足workerCountOf(c) < corePoolSize或addWorker失败,进入步骤2
    // 校验线程池是否是Running状态且任务是否成功放入workQueue(阻塞队列)
    if (isRunning(c) && 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 方法

「流程图:」

图片

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

「源码详读:」

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

        /** 
         * 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(c);
            // 校验线程池已有线程数量是否超限:
            // 1.线程池最大上限CAPACITY 
            // 2.corePoolSize或maximumPoolSize(取决于入参core)
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize)) 
                return false;
            // 通过CAS操作使工作线程数+1,跳出外层循环
            if (compareAndIncrementWorkerCount(c)) 
                break retry;
            // 线程+1失败,重读ctl
            c = ctl.get();   // Re-read ctl
            // 如果此时线程池状态不再是running,则重新进行外层循环
            if (runStateOf(c) != 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(c);

    // 再次检验线程池是否是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(); }
 
    /**


### 一、网安学习成长路线图


网安所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/aa7be04dc8684d7ea43acc0151aebbf1.png)


### 二、网安视频合集


观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/f0aeee2eec7a48f4ad7d083932cb095d.png)


### 三、精品网安学习书籍


当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/078ea1d4cda342f496f9276a4cda5fcf.png)


### 四、网络安全源码合集+工具包


光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/e54c0bac8f3049928b488dc1e5080fc5.png)


### 五、网络安全面试题


最后就是大家最关心的网络安全面试题板块  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/15c1192cad414044b4dd41f3df44433d.png)![在这里插入图片描述](https://img-blog.csdnimg.cn/b07abbfab1fd4edc800d7db3eabb956e.png)  



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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值