前文中了解到AQS借助LockSupport.park和LockSupport.unpark完成线程的堵塞和唤醒,那么LockSupport内部又是怎么完成的?这是一个什么类?

LockSupport是用于运用锁堵塞线程的根底完成,是其他同步类的根底,这个类为每个运用它的线程关联一个答应证(有点类似于Semaphore),假如答应证可用,线程调用park方法时会立即返回,线程正常履行,否则当时线程堵塞,直到有其他线程调用unpark使得答应证可用,此刻线程被唤醒,再次测验获取答应证,其内部定义的park和unpark方法供给了堵塞和处理堵塞的根本完成。

LockSupport常见函数如下表所示:

函数称号 阐明 备注
void park() 堵塞当时线程 在下列情况发生时唤醒: 1.调用unpark函数,开释该线程的答应; 2.该线程被中止; 3.设置的堵塞超时时刻耗尽; 4.抵达设置的指定时刻
void park(Object blocker) 运用指定的blocker目标堵塞当时线程 唤醒条件,park中已阐明
void parkNanos(long nanos) 堵塞当时线程直到超时时刻耗尽,nanos为指定的超时时刻 唤醒条件,park中已阐明
void parkNanos(Object blocker, long nanos) 在超时时刻耗尽前,运用指定的blocker目标堵塞当时线程,假如在抵达超时时刻后,答应仍不可用,则完毕堵塞 唤醒条件,park中已阐明
void parkUntil(long deadline) 在指定时刻前,堵塞该线程,假如在抵达指定时刻后,答应仍不可用,则完毕堵塞 唤醒条件,park中已阐明
void parkUntil(Object blocker, long deadline) 在指定时刻前,运用指定的目标堵塞该线程,假如在抵达指定时刻后,答应仍不可用,则完毕堵塞 唤醒条件,park中已阐明
void unpark(Thread thread) 用于唤醒传入的在堵塞中的线程 /
Object getBlocker(Thread t) 获取当时线程的堵塞目标 /
void setBlocker(Thread t, Object arg) 运用指定目标为线程设置堵塞目标 /

LockSupport.park

ReentrantLock中调用LockSupport.park代码如下所示:

// AbstractQueuedSynchronizer.java
private final boolean parkAndCheckInterrupt() {
  LockSupport.park(this);
  return Thread.interrupted();
}

在LockSupport中,void park(Object blocker)完成代码如下:

// LockSupport.java
public static void park(Object blocker) {
  Thread t = Thread.currentThread();
  setBlocker(t, blocker);
  UNSAFE.park(false, 0L);
  setBlocker(t, null);
}

能够看到在park流程中主要包含以下过程:

  1. 获取当时线程

  2. 将传入的目标设置为该线程的parkBlocker

    setBlocker函数完成如下所示:

    private static void setBlocker(Thread t, Object arg) {
      // Even though volatile, hotspot doesn't need a write barrier here.
      UNSAFE.putObject(t, parkBlockerOffset, arg);
    }
    

    能够看到这儿新出现了UNSAFE和parkBlockerOffset两个标识,这两个是用来干嘛的?咱们一起看看其声明的代码:

    private static final sun.misc.Unsafe UNSAFE;
    private static final long parkBlockerOffset;
    
    static {
      try {
        UNSAFE = sun.misc.Unsafe.getUnsafe();
        Class<?> tk = Thread.class;
        parkBlockerOffset = UNSAFE.objectFieldOffset
           (tk.getDeclaredField("parkBlocker"));
         .....
       } catch (Exception ex) { throw new Error(ex); }
    }
    

    能够看到UNSAFE目标是经过Unsafe.getUnsafe()获取的,那么Unsafe这个类到底是干嘛的?

    大家都知道Java目标在内存中创立,大多数情况下咱们都是经过类的目标去修改和拜访内存中的数据的,那么假如需要直接从内存修改某一目标的取值,应该怎么做呢?就是运用Unsafe类,该类只允许在JDK信任的类中调用(当时也能够用反射实例化该类目标)。

    在Unsafe类中定义了两个重要函数park和unpark,其中park用于完成线程堵塞,unpark用于完成线程唤醒(Unsafe本质上是操作线程的Parker目标来完成线程堵塞和唤醒的,具体见参阅链接,了解即可),上文中的parkBlockerOffset正是定义了Thread类的parkBlocker特点成员的内存偏移量,运用该值再结合Unsafe目标就能够完成直接操作内存中的parkBlocker值的意图,Thread类中的parkBlocker声明如下:

    // Thread.java
    volatile Object parkBlocker;
    

    能够得到这一环节主要是将AQS作为blocker设置到当时线程的parkBlocker成员特点上。

    CAS底层也是经过Unsafe履行的

  3. 履行UNSAFE.park

    结合上文可知,这步完成后,当时线程堵塞

  4. 设置线程的parkBlocker为null

    第三步中线程处于堵塞状况,当然就不能履行设置parkBlocker为null的操作了,那么什么时候履行呢?当线程从堵塞状况唤醒时,履行该过程,使得线程的parkBlocker目标恢复初始状况。

LockSupport.unpark

LockSupport.unpark代码如下所示:

// LockSupport.java
public static void unpark(Thread thread) {
  if (thread != null)
    UNSAFE.unpark(thread);
}

能够看出当传入的线程不为空时,履行Unsafe的park函数唤醒当时线程,取消堵塞,此刻持续履行park函数中的setBlocker(null),将parkBlocker成员设置为null。

参阅链接

blog.csdn.net/a7980718/ar…