线程池提交和执行任务源码探究

说明:
本文主要探究任务提交到线程池,以及任务如何被执行,以及如何使用 Future 来获取任务执行的返回结果。

一、线程池的好处

1、线程池的重用

线程池的创建和销毁的开销巨大,通过线程池的重用大大减少了这些不必要的开销,既然减少了内存开销,其线程执行速度也回提高。

2、控制线程池的并发数

控制线程池的并发数可以有效的避免大量的线程池争夺CPU资源而造成堵塞。

3、线程池也可以对线程进行管理

线程池可以提供定时、定期、单线程、并发数控制等功能。比如通过SheduledThreadPool线程池来执行S秒后,每隔N秒执行一次的任务。

二、线程池使用方式

1、编写好线程池配置,并将执行器注入到容器
@Configuration
public class SourceThreadPoolConfig {

   @Value("${srm-source.core-pool-size:4}")
   private Integer core;
   @Value("${srm-source.max-pool-size:10}")
   private Integer maxPoolSize;
   @Value("${srm-source.keep-alive-seconds:300}")
   private Integer keepAliveSeconds;
   @Value("${srm-source.queue-capacity:200}")
   private Integer queueCapacity;
   @Value("${srm-source.thread-name-prefix:srm-source-thread-execute}")
   private String threadNamePrefix;

   @Bean
   @Qualifier("executor")
   public ThreadPoolTaskExecutor executor() {
      final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
      // 设置核心线程数
      executor.setCorePoolSize(core);
      // 设置最大线程数
      executor.setMaxPoolSize(maxPoolSize);
      // 除核心线程外的线程存活时间
      executor.setKeepAliveSeconds(keepAliveSeconds);
      // 如果传入值大于0,底层队列使用的是LinkedBlockingQueue,否则默认使用SynchronousQueue
      executor.setQueueCapacity(queueCapacity);
      // 线程名称前缀
      executor.setThreadNamePrefix(threadNamePrefix);
      // 设置拒绝策略。当线程池中没有线程的时候调用当前线程池的线程去执行任务
      executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
      return executor;
   }
}
2、直接@Autowire注入即可使用
public class Demo{
  // 直接注入线程池
  @Autowired
  @Qualifier("executor")
  private ThreadPoolExecutor executor;

  public void test(){
    // 直接将任务提交到线程池,交给线程池去执行
    executor.submit(()->{
      // 具体业务逻辑
      // xxxxx
    });
  }
}

三、线程提交的基础流程

我们要向线程池提交一个任务,我们一般都是使用 executor.submit(runnable / callable)的方式提交任务到线程池中。首先需要明白线程池执行线程的基础流程,如下:

①、当一个任务提交到线程池以后,首先判断线程池中当前工作的线程数量有没有达到核心线程数量。

②、如果没有,则调用本地方法(native方法),向操作系统申请创建一个线程用于执行该任务。

③、如果有,则将该线程封装成一个node,然后添加到等待队列中(具体添加的逻辑还比较复杂)。

④、如果等待队列已经满了,则检查线程池中的线程数量是否已经达到了最大线程数量。

1、若已经达到了最大线程数量,则执行拒绝策略(可自己定义拒绝策略,也可以使用默认的拒绝策略)。

2、若没有,则继续开启线程去执行任务,直到达到最大线程数量

在这里插入图片描述

ThreadPoolExecutor.submit()方法为例,进行如下探究!

四、任务提交到线程池

一般我们都是通过 ThreadPoolExecutor.submit() 的方式将任务提交到线程池!

一个任务提交到线程池,大致分为三步:

1、将任务封装 为 FutureTask

2、执行 execute

3、返回

A)、封装为 FutureTask

1、submit方法

可以看到 submit 方法有三个重载方法,该方法可以提交 Runnable 和 Callable 类型的任务。不管是提交的 Runnable 还是 Callable 类型的任务,都会在该方法中被封装为一个 FutureTask 然后提交到线程池中。

public abstract class AbstractExecutorService implements ExecutorService {
  
    // submit 重载方法1
  	public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        // ①、提交到线程池的时候会将 Runnable 任务封装为一个 FutureTask
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        // ②、执行。后面详细介绍
        execute(ftask);
        // ③、返回 Future ,可通过 Future.get到线程执行后的返回值(这里的返回值就是 null )
        return ftask;
    }
  
		// submit 重载方法2
    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        // ①、提交到线程池的时候会将 Runnable 任务封装为一个 FutureTask
        RunnableFuture<T> ftask = newTaskFor(task, result);
        // ②、执行。后面详细介绍
        execute(ftask);
        // ③、返回 Future ,可通过 Future.get到线程执行后的返回值(这里的返回值就是传进来的 result )
        return ftask;
    }
  
		// submit 重载方法3
    public <T> Future<T> submit(Callable<T> task) {
        if ①、(task == null) throw new NullPointerException();
        // 提交到线程池的时候会将 Callable 任务封装为一个 FutureTask
        RunnableFuture<T> ftask = newTaskFor(task);
      	// ②、执行。后面详细介绍
        execute(ftask);
        // ③、返回 Future ,可通过 Future.get到线程执行后的返回值
        return ftask;
    }
}
2、将 Callable 或 Runnable封装为一个FutureTask

不管是提交的 Runnable 还是 Callable 类型的任务,都会在该方法中被封装为一个 FutureTask!!!

①、newTaskFor
public abstract class AbstractExecutorService implements ExecutorService {
  
  // 提交到线程池的时候会将 Callable 任务封装为一个 FutureTask
  protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
      // 将 callable 封装为一个 FutureTask
      return new FutureTask<T>(callable);
  }

  // 提交到线程池的时候会将 runnable 任务适配为 Callable 并封装为一个 FutureTask
  protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    // 将 Runnable 适配为 callable,并且将提交的Runnable任务封装为 FutureTask
    return new FutureTask<T>(runnable, value);
  }
}
②、具体封装为 FutureTask 的方法

可以看到 FutureTask 有两个构造函数,分别是对应处理 RunnableCallable的。

①、如果是runnable接口,则先将runnable接口适配为callable接口,然后再设置到 this.callable 变量上。

②、如果是 Callable 接口,则直接设置到 this.callable 变量上。

public class FutureTask<V> implements RunnableFuture<V> {
  	
    // callable接口并绑定到callable变量上
    private Callable<V> callable;
  
  	// ①、处理 Runnable 接口
    public FutureTask(Runnable runnable, V result) {
        // 如果新建 FutureTask 对象的时候传入的是 runnable ,
      	// 则通过 Executors.callable(runnable, result)将 runnable 适配为 callable
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

  	// ②、处理 callable 接口
    public FutureTask(Callable<V> callable) {
      if (callable == null)
        throw new NullPointerException();
      this.callable = callable;
      this.state = NEW;       // ensure visibility of callable
    }
}
③、将Runnable适配为Callable
public class Executors {
    public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        // 将 Runnable 接口进行包装,适配为 callable
        return new RunnableAdapter<T>(task, result);
    }

    /**
     * 将 Runnable 接口进行包装,适配为 callable
     */
    public static Callable<Object> callable(Runnable task) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<Object>(task, null);
    }
  
  	/**
     * A callable that runs given task and returns given result
     * Runnable 接口适配器,实现了 Callable 接口。将 Runnable 适配为 Callable
     */
    static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            // 在call方法里调用 runnable的run方法。适配
            task.run();
            // 直接将传递过来的 result 返回给你,不做任何处理
            return result;
        }
    }
}

B)、执行 execute()

执行流程分三步

  1. 在执行的线程数 < corePoolSize,新建线程。调用方法addWorker 自动检查运行状态和wokerCount数量。防止错误情况下增加线程,返回false。

  2. 如果一个任务成功增加到队列中,仍需要双重检测是否应该增加线程(因为存在上次检测后死掉的线程)或者线程池从上次进入这个方法时停止。所以需要再次检测状态,必要的时候回滚或者队列没有线程是要启动新的线程。

  3. 队列中无法增加新的任务,尝试新增一个线程,如果失败应该是停止或者队列饱和,所以拒绝这个任务。

下面的代码中的 addWorker 方法是重点方法,需要重点探究!!!

/**
 * 据线程池的状态以及当前线程数(workerCount)判断是否需要新增一个worker,
 * 还是直接放入等待队列,还是执行拒绝策略。
 */
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
  
    // 【 1 】、worker数量比核心线程数小,直接创建worker执行任务
    if (workerCountOf(c) < corePoolSize) {
        // addWorker(command, true): 创建一个worker节点封装该任务,并执行该任务.该方法是重点方法
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
  
    // 【 2 】、worker数量超过核心线程数,任务直接进入队列。
    // workQueue.offer(command) 是添加到等待队列,如果添加成功了,还需要进行双重检查,检查是否该为该任务开启一个线程去执行他
    // 条件成立: 线程池的状态是Running,并且放入到线程等待队列成功了
    if (isRunning(c) && workQueue.offer(command)) {
        // 获取线程池状态 和 工作线程数量
        int recheck = ctl.get();
        // 线程池状态不是RUNNING状态,说明执行过shutdown命令,需要对新加入的任务执行reject()操作。
        // 这儿为什么需要recheck,是因为任务入队列前后,线程池的状态可能会发生变化。
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 这儿为什么需要判断0值,主要是在线程池构造方法中,核心线程数允许为0
        else if (workerCountOf(recheck) == 0)
            // 如果检查通过,则开启一个线程来执行该任务
            addWorker(null, false);
    }
  
    //【 3 】、 如果线程池不是运行状态,或者任务进入队列失败,则尝试创建worker执行任务(即:线程阻塞队列满了但线程池中的线程数没达到最大线程数,
    // 则新开启一个线程去执行该任务)。
    // 这儿有3点需要注意:
    // 1. 线程池不是运行状态时,addWorker内部会判断线程池状态
    // 2. addWorker第2个参数表示是否创建核心线程
    // 3. addWorker返回false,则说明任务执行失败,需要执行reject操作
    else if (!addWorker(command, false))
        // 拒绝该任务
        reject(command);
}

C)、addWorker

将要执行的任务 FutureTask包装成一个worker节点,并且执行该任务(如果条件成立)。

该方法会检查线程池状态,线程数量等信息,然后判断是否能够创建一个worker,如果判断通过,则创建一个worker并且向操作系统申请开启一个线程(或复用线程池中的线程)来执行任务!

/**
 * firstTask : 表示要执行的任务
 * core : 表示是否创建核心线程
 */
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        // 获取线程池状态
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        for (;;) {
            // 获取线程池中工作线程的数量
            int wc = workerCountOf(c);
            // 如果线程池中工作线程数量大于等于线程池容量 或者 大于等于corePoolSize 
            // 或 maximumPoolSize 则添加worker失败
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 将线程池中工作线程数量加一,然后跳出双重for循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            // 检查此时的线程池状态和第一次获取的线程池状态rs是否相等,若相等则继续for循环,
            // 若不相等则跳出内层for循环重新获取
            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 {
        // 将任务包装成一个worker节点。将任务 firstTask 封装到 Worker 对象的 firstTask 属性中
        // 后面线程真正执行任务的时候其实调用的就是 Worker.firstTask.run()方法
        w = new Worker(firstTask);
        // 获取到 Worker 代表的线程
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            // 加锁
            mainLock.lock();
            try {
                // 获取线程池运行状态
                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
                    // isAlive() 是native方法
                    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();
            }
            // 如果worker成功的被添加到了线程池的正在工作线程集合,则直接调用 Thread.start() 运行任务
            if (workerAdded) {
              	// =======================这步就是开启一个线程去执行任务=======================
                //【 开启线程执行任务 】调用 Thread.start() 方法,向操作系统申请线程(或复用线程池中的线程)执行任务
              //  `t.start();`代码,该代码的含义就是调用native方法,向操作系统申请线程一个线程来执行任务(或复用线程池中的线程)。
                t.start();
                // 设置任务已被开启
                workerStarted = true;
            }
        }
    } finally {
        // 如果开启线程失败,则将该worker节点从工作线程node的集合中移除
        if (! workerStarted)
            addWorkerFailed(w);
    }
    // 返回线程是否成功开启
    return workerStarted;
}

五、线程池执行已提交的任务

①、当一个任务提交到线程池,首先会被封装成一个worker对象(不管是否立即执行该任务)

②、根据线程池中的线程数量,线程池状态等判断该任务(worker)是否被 立即执行 或 进入等待队列 或 拒绝。

  • 如果一个任务被立即执行,则立即执行该worker
  • 如果一个任务是被添加到等待队列中了,但此时线程池中有空闲线程了,便会从等待队列中取出该worker并执行。
  • 如果一个任务被拒绝了,则不会被执行。

此时执行该任务的worker调用的其实就是Worker里的run方法。

③、根据该任务的执行情况:1、正常执行 2、发生异常 将结果封装到 outcome 变量中!

1、worker和futuretask和callable / runnable 的关系

要先理解这三者之间的关系,后面才能理解为什么任务执行的时候先调用worker 的run方法,然后再调用的futuretask的run方法,最后再调用的 任务的run方法!

**说明:**我们提交给线程池的任务会先被封装为了 FutureTask ,然后被封装成一个 Worker 。所以在线程池执行我们提交的任务的时候一定也是执行的Worker里的 FutureTask的run方法。所以我们需要关注: Worker.runFutureTask.run!!!

在这里插入图片描述
在这里插入图片描述

2、Worker.run()
// 任务提交后会被封装为一个 worker节点
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
   
    /** Thread this worker is running in.  Null if factory fails. */
    final Thread thread;
   	// 具体任务的引用会被记录到这个属性上(即将 FutureTask 对象封装到该属性上)
    Runnable firstTask;
   
    Worker(Runnable firstTask) {
        // 将任务赋值给 firstTask 属性
        this.firstTask = firstTask;
        // 这儿是Worker的关键所在,使用了线程工厂创建了一个线程。传入的参数为当前worker
        this.thread = getThreadFactory().newThread(this);
    }

    /** Delegates main run loop to outer runWorker  */
    // 代理
  	// 【当线程执行任务的时候就会执行这个方法】
    public void run() {
        // 执行 worker的run 方法
        runWorker(this);
    }
  
  	// 执行任务
  	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 ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&  !wt.isInterrupted())
                wt.interrupt();
            try {
                // 执行run方法之前
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // 【 调用run方法运行任务。这里其实是调用的 futureTask 的 run 方法。 】
                    task.run();
                } catch (Exception x) {
                    // 省略.....
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                // help GC
                task = null;
                // 将已执行的任务数量++
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
	}
}
3、FutureTask.run()

在上一步的 task.run(); 代码中便会调用到 FutureTask.run() 方法!如下

public class FutureTask<V> implements RunnableFuture<V> {
  
  	// submit(callable / runnable) runnable接口使用适配器模式适配成了 callable
    private Callable<V> callable;
    
  	// 任务执行的返回值会被保存到该变量中(比如: 我们的 Callable 类型的任务是可以接收返回值的)
    // 任务正常执行情况下:outcome 保存了任务的执行返回结果,即callable.call的返回值
    // 任务异常执行情况下:outcome 保存了任务执行时抛出的异常, 即 callable.call 方法抛出的异常
    private Object outcome;
   
    // 当前任务被线程执行期间,保存当前执行任务的线程对象引用
    private volatile Thread runner;
    
    private volatile WaitNode waiters;
  
  	// ============= 线程真正执行任务的时候会调用该方法 =============
    public void run() {
        // 条件一:若 state != NEW 成立,则说明当前 task 已经被执行过了,
      	// 或者已经被取消了,总之就是,非 NEW 状态的任务,线程就不处理了
        // 条件二:!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()) 条件成立,则说明 cas 设置 runnerOffset失败,说明当前任务被其他线程抢占了。
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        // 执行到这里,说明当前状态一定是 NEW 状态,并且当前线程抢占 Task 也成功
        try {
            Callable<V> c = callable;
            // c != nul 防止空指针异常
            // state == NEW 防止外部线程取消掉当前任务
            if (c != null && state == NEW) {
                // 结果引用
                V result;
                // true 表示 callable.run 代码块执行成功,未抛出异常
                // false 表示 callable.run 代码块执行失败,抛出异常
                boolean ran;
                try {
                    // 【 执行任务 】调用我们自己实现的callable接口的call,
                  	// 或是经过适配后的runnable接口的call方法
                    result = c.call();
                    // 执行成功则修改 ran 状态为 true
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    // 执行失败则修改 ran 状态为 false
                    ran = false;
                    // 设置异常信息,将异常信息封装到 outcome 中
                    setException(ex);
                }
                // 如果 ran = true 则说明 c.call()代码正常执行结束了
                if (ran)
                    // 设置线程执行返回结果到 outcome 属性中
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
}

六、外部线程获取任务的执行结果

当我们提交任务到线程池后,会返回一个 Future ,我们可以根据这个 Future 来获取任务执行的返回结果,执行状态 或 取消执行等!

// 提交到线程池
Future<?> future = executorService.submit(futureTask);
// 获取任务执行结果
Object result = future.get();
// 带超时时间的获取任务执行结果方法。和get()方法一样,只是多了一个超时时间
future.get(100,TimeUnit.MICROSECONDS);
// 如果任务还没有被执行,可以调用 cancel 方法取消该任务
future.cancel(true);
// 判断该任务是否已经被取消了
future.isCancelled();
1、外部线程获取任务返回值(get())

通过future.get();方法即可获取到任务执行的返回值。

①、如果任务已经执行完毕并且有了返回值(有可能是正常执行完毕,有可能是发生异常),则直接返回结果。

②、如果当前任务还没有被执行 或 没有执行完毕!则调用 Future.get() 方法的线程会被封装为一个 WaitNode 并且阻塞 (LockSupport.park(this)),直到该任务执行完毕后被唤醒(也可能会被中断),然后取到返回值。

/**
 * @throws CancellationException {@inheritDoc}
 * 该方法是外部线程调用 get
 * 可能会有 多个线程 等待着get这个任务执行的返回值
 * 场景:多个线程等待当前任务执行完后的结果....
 */
public V get() throws InterruptedException, ExecutionException {
    // 获取当前任务状态
    int s = state;
    // 如果 s <= COMPLETING ,则说明未执行、正在执行、正在完成(总之就是当前任务还没执行结束)
    // 则外部想要获取到当前任务的返回结果的外部线程会被阻塞在这里
    if (s <= COMPLETING)
        // 不带超时的等待,返回当前任务的状态
        s = awaitDone(false, 0L);
    // 返回 callable.call 方法的执行结果
    return report(s);
}
2、awaitDone方法,阻塞调用线程

该方法用于当外部线程想要获取任务的执行结果的时候,如果当前任务还没有执行完毕,则将外部线程封装为一个 WaitNode 加入到等待队列中。

/**
 * Awaits completion or aborts on interrupt or timeout.
 * 该方法的作用:
 * 该方法用于当外部线程想要获取任务的执行结果的时候,如果当前任务还没有执行完毕,则将外部线程加入到等待队列中
 */
private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    // 0 表示不带超时
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    // 引用当前线程,封装成 WaitNode 对象
    WaitNode q = null;
    // 表示当前线程WaitNode对象没有入队
    boolean queued = false;

    // 自旋
    for (;;) {
        // 如果 Thread.interrupted() 条件成立,则说明当前线程是被其他线程使用 中断这种方式 唤醒的
        // interrupted()方法返回true后,会将Thread中的中断标记重置回false
        if (Thread.interrupted()) {
            // 将当前外部线程的node从等待队列中移除。
            removeWaiter(q);
            // 向外部调用这个方法的方法抛出中断异常
            throw new InterruptedException();
        }

        // 加入当前线程是被其他线程使用 unpark(thread)的方式唤醒的,会正常自旋,走下面的逻辑
        int s = state;
        // ①、条件成立,则说明当前任务已经有了结果。可能是好结果也可能是异常.....
        if (s > COMPLETING) {
            // 条件成立,则说明已经为当前线程创建过node了,此时需要 q.thread = null (help GC)
            if (q != null)
                q.thread = null;
            // 直接返回当前状态
            return s;
        }
        // ②、当前任务接近完成,或接近失败状态。这里让当前线程再次释放cpu,进入到下一次cpu争抢....
        else if (s == COMPLETING) // cannot time out yet
            Thread.yield();
      
        // ③、第一次自旋进入这里,为外部调用get()方法获取结果的线程创建一个node,将线程封装为node
        else if (q == null)
            q = new WaitNode();
      
        // ④、第二次自旋进入这里,如果该线程对应的node节点还没有入队,则使用cas的方式入队(waiters一直指向等待队列的头节点)
        else if (!queued){
            // 入队
            q.next = waiters;
            // 返回是否入队成功
            // 这里 waiters 指向等待队列头节点,上一步已经将 q.next 指向了 waiters 了,这里是通过cas的方式将 waiters 和 q交换
            // 使用cas的方式将 waiters 指向当前节点,如果成功了则返回true,如果失败了则说明可能其他线程已经先一步将waiters指向他的node了,需要重试
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset, waiters, q);
        }
      
        // ⑤、第三次自旋进入这里(如果是带等待时间的)
        else if (timed) {
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                removeWaiter(q);
                return state;
            }
            // 这里只将线程 park nanos 时间
            LockSupport.parkNanos(this, nanos);
        }
      
        // ⑥、当前执行get操作的线程就会被park掉,线程状态就会变成 waitting 状态了,相当于休眠了.....
        // 除非有其他的线程将你唤醒,或将当前线程中断。注意和上面一句代码对比
        else
            // 将线程park掉
            LockSupport.park(this);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值