目录
AQS
java里大部分的同步锁都是基于AbstractQueuedSynchronizer(简称AQS)实现的,就比如我们经常的遇到的ReentrantLock,ReentrantReadWriteLock,CountDownLatch。
AQS的核心思想就是使用一个队列(FIFO)来保存线程,当某个线程需要上锁的时后,如果锁被占用,就把这个线程加入到队列里,然后把这个线程挂起。当锁被释放的时侯,AQS会从队列里取出一个线程然后唤醒,让线程重新尝试获取锁。
公平锁和非公平锁
公平锁:每个线程获取锁的顺序都是按照线程访问的先后来决定的,最先获取锁的线程都是最前面的。
非公平锁:每个线程获取锁的顺序都是随机的,不会向公平锁一样遵循“先来后到”,所有线程相互竞争,由cpu决定哪个线程能获取到锁。
ReentrantLock定义锁
ReentrantLock默认是非公平锁,但是我们在创建 ReentrantLock 时,可以手动指定为公平锁。如下源码示例:
入参设置为true, 就可以设定成公平锁
加锁过程
非公平锁:NonfairSyc类(内部类)的tryAcquire(尝试获取锁)方法里,调用了抽象类Sync中的普通方法nonfairTryAcquire,入参是锁状态的更新值,主要逻辑就是获取锁的状态值state,如果是0,就通过CAS机制尝试获取锁,获取成功就上锁成功,如果锁已经被独占,就获取占有锁的线程对象,在原本状态值的基础上增加重入次数,如果小于0,就代表超出锁计数范围,反之,更新state状态值,这个线程仍然持有这个锁。如下源码示例:
公平锁:在内部类FairSync中,尝试获取公平锁的逻辑是,先判断状态是不是0,是0就表示没有线程占用锁,不是就表示锁已经被占用,没有占用当前线程就尝试占用锁,先判断sync队列(AbstractQueuedSynchronizer中的数据结构)里有没有等待时间更长的线程以及期望值和更新值是否一致。满足条件,就修改参数,也就是获取锁,不满足,则获取锁失败。如果锁被占用,具体逻辑和非公平锁的流程一样。如下源码示例: