AQS基础

快速一览
  • 基础定义
  • 前置知识:锁实现的原理——LockSupport
  • AQS队列管理器
  • 公平锁和非公平锁
  • 共享锁与排他锁
  • 参考文献
  • 未完待续
基础定义

AQS:AbstractQueuedSynchronizer 抽象队列同步器,是所有锁的基础。

前置知识:锁实现的原理——LockSupport

Java 的线程阻塞和唤醒是通过 Unsafe 类的 park 和 unpark 方法做到的。

public class Unsafe {
  ...
  public native void park(boolean isAbsolute, long time);
  public native void unpark(Thread t);
  ...
}

锁可以暂停线程的奥秘所在正是因为锁在底层调用了 park 方法。

线程对象 Thread 里面有一个重要的属性 parkBlocker(volatile修饰),它保存当前线程因为什么而 park。当线程被 unpark 唤醒后,这个属性会被置为 null。Unsafe.park 和 unpark 并不会帮我们设置 parkBlocker 属性,负责管理这个属性的工具类是 LockSupport,它对 Unsafe 这两个方法进行了简单的包装。

class LockSupport {
  ...
  public static void park(Object blocker) {
     Thread t = Thread.currentThread();
     setBlocker(t, blocker);
     U.park(false, 0L);
     setBlocker(t, null); // 醒来后置null
  }

  public static void unpark(Thread thread) {
     if (thread != null)
        U.unpark(thread);
     }
  }
  ...
}
AQS队列管理器

内部主要有两个部分:持有当前锁的线程和统计的次数、维持竞争失败的锁的队列。

基本原理:当前资源state=0,线程会试图获取锁,并通过CAS修改资源的state,若成功从0-1,那么当前锁的持有线程将改为当前线程(exclusiveOwnerThread原本为null)。之后其他线程竞争锁时,先看state是不是0,不是的话再看持有者是不是自己,不是的话就被记录到一个双向队列里面去(失败者队伍)。当前锁被释放后,会唤醒队首的线程,让它去获取。

公平锁和非公平锁

公平锁会在队伍有其他线程的时候进入队列等待;非公平锁会先去竞争一次,失败了才等待。(根据我看可重入锁的代码,他其实是竞争了两次,但是道理是一样的)

非公平锁就是抢夺在队首线程由park()方法返回到加锁这中间的可能性。

==Q:如果被抢走了,这时候这个线程待在队首还是重新排队呀?==

A:在队首,可以看到获取锁AcquireQueued()的方法是一个自旋的方法,如果没有获取锁就会一直尝试获取。

共享锁与排他锁

ReentrantLock的锁是排他锁,一个线程持有,其它线程都必须等待。

而 ReadWriteLock里面的读锁不是排他锁,它允许多线程同时持有读锁,这是共享锁。

共享锁和排他锁是通过 Node 类里面的 nextWaiter 字段区分的(EXCLUSIVE和SHARED)。

class AQS {
  static final Node SHARED = new Node();
  static final Node EXCLUSIVE = null;

  boolean isShared() {
    return this.nextWaiter == SHARED;
  }
}
//nextWaiter还有其他功能。在等待队列里面,它表示下一个node
参考文献

链接:https://zhuanlan.zhihu.com/p/52280869

链接:https://zhuanlan.zhihu.com/p/43423666

未完待续
  1. AQS队列维护源码
  2. 条件变量地方不明白
  3. CAS

你可能感兴趣的:(AQS基础)