快速一览
- 基础定义
- 前置知识:锁实现的原理——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
未完待续
- AQS队列维护源码
- 条件变量地方不明白
- CAS