1.Lock

本身是一个接口

  1. public interface Lock {
  2. void lock();
  3. void lockInterruptibly() throws InterruptedException;
  4. boolean tryLock();
  5. boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
  6. void unlock();
  7. Condition newCondition();
  8. }
  • lock 获取锁,如果获取不到就阻塞
  • lockInterruptibly 获取锁,如果获取不到就阻塞线程。并且检查线程中断情况。如果有中断,在获取锁后,抛出异常 InterruptedException ,然后回重置中断标志。
  • tryLock 尝试获取锁,立马返回结果。
  • tryLock(long time, TimeUnit unit) 尝试获取锁,获取到就返回true,如果获取不到,就阻塞线程,并有设置有超时时间。超时后,返回获取结果。也会检查线程中断情况。
  • unlock 释放锁,释放成功,唤醒后继节点线程。
  • newCondition 创建一个绑定在当前Lock对象上的Condition对象,这说明Condition对象和Lock对象是对应的,一个Lock对象可以创建多个Condition对象,它们是一个对多的关系。

image.png
具体实现类如下图
image.png

2.ReentrantLock

多线程-5.5 锁-ReentrantLock

3.Condition

多线程-5.6 锁-Condition

4.ReentrantReadWriteLock

内部主要有五个内部类, Sync、FairSync、NonfairSync、ReadLock、WriteLock。
ReadLock、WriteLock 分别继承了 Lock 接口。
ReadLock 使用的是共享模式,WriteLock 使用的独占模式。在 AQS 中 state 代表了锁被线程持有的状态。那么一个字段如何来同时代表两种锁模式的状态呢?答案是同过位运算,在 state 的 32 个位数中,高 16 位表示共享模式读的数量,低 16 位表示独占模式写的数量

  1. public class ReentrantReadWriteLock
  2. implements ReadWriteLock, java.io.Serializable {
  3. private static final long serialVersionUID = -6992448646407690164L;
  4. /** Inner class providing readlock */
  5. private final ReentrantReadWriteLock.ReadLock readerLock;
  6. /** Inner class providing writelock */
  7. private final ReentrantReadWriteLock.WriteLock writerLock;
  8. /** Performs all synchronization mechanics */
  9. final Sync sync;
  10. public ReentrantReadWriteLock() {
  11. this(false);
  12. }
  13. public ReentrantReadWriteLock(boolean fair) {
  14. //创建锁实际的操作类型
  15. sync = fair ? new FairSync() : new NonfairSync();
  16. readerLock = new ReadLock(this);
  17. writerLock = new WriteLock(this);
  18. }
  19. }

Sync

  1. abstract static class Sync extends AbstractQueuedSynchronizer {
  2. /*
  3. * Read vs write count extraction constants and functions.
  4. * Lock state is logically divided into two unsigned shorts:
  5. * The lower one representing the exclusive (writer) lock hold count,
  6. * and the upper the shared (reader) hold count.
  7. */
  8. //通过这几个字段和 state 进行位运算,来计算出 共享模式、独占模式线程持有锁的状态。
  9. static final int SHARED_SHIFT = 16;
  10. static final int SHARED_UNIT = (1 << SHARED_SHIFT);//00000000 00000001 00000000 00000000
  11. static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;//00000000 00000000 11111111 11111111
  12. static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;//00000000 00000000 11111111 11111111
  13. static int sharedCount(int c) { return c >>> SHARED_SHIFT; }//共享线程的数量
  14. static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }//独占线程的数量
  15. }

ReadLock

  1. public static class ReadLock implements Lock, java.io.Serializable {
  2. private final Sync sync;
  3. protected ReadLock(ReentrantReadWriteLock lock) {
  4. sync = lock.sync;
  5. }
  6. public void lock() {
  7. sync.acquireShared(1);
  8. }
  9. public void lockInterruptibly() throws InterruptedException {
  10. sync.acquireSharedInterruptibly(1);
  11. }
  12. public boolean tryLock() {
  13. return sync.tryReadLock();
  14. }
  15. public boolean tryLock(long timeout, TimeUnit unit)
  16. throws InterruptedException {
  17. return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
  18. }
  19. public void unlock() {
  20. sync.releaseShared(1);
  21. }
  22. public Condition newCondition() {
  23. throw new UnsupportedOperationException();
  24. }
  25. }

WriteLock

  1. public static class WriteLock implements Lock, java.io.Serializable {
  2. private final Sync sync;
  3. protected WriteLock(ReentrantReadWriteLock lock) {
  4. sync = lock.sync;
  5. }
  6. public void lock() {
  7. sync.acquire(1);
  8. }
  9. public void lockInterruptibly() throws InterruptedException {
  10. sync.acquireInterruptibly(1);
  11. }
  12. public boolean tryLock( ) {
  13. return sync.tryWriteLock();
  14. }
  15. public boolean tryLock(long timeout, TimeUnit unit)
  16. throws InterruptedException {
  17. return sync.tryAcquireNanos(1, unit.toNanos(timeout));
  18. }
  19. public void unlock() {
  20. sync.release(1);
  21. }
  22. public Condition newCondition() {
  23. return sync.newCondition();
  24. }
  25. public boolean isHeldByCurrentThread() {
  26. return sync.isHeldExclusively();
  27. }
  28. public int getHoldCount() {
  29. return sync.getWriteHoldCount();
  30. }
  31. }

可以看出读写锁的具体操作逻辑都是由 Sync 实现的。其它逻辑都在 AQS 的共享锁中有过分析。

写锁是一个独占锁,它在获取锁的逻辑和 ReentrantLock 的独占锁获取锁的逻辑不太一样。

tryAcquire

  1. //ReentrantReadWriteLock.Sync.java
  2. protected final boolean tryAcquire(int acquires) {
  3. /*
  4. * Walkthrough:
  5. * 1. If read count nonzero or write count nonzero
  6. * and owner is a different thread, fail.
  7. * 2. If count would saturate, fail. (This can only
  8. * happen if count is already nonzero.)
  9. * 3. Otherwise, this thread is eligible for lock if
  10. * it is either a reentrant acquire or
  11. * queue policy allows it. If so, update state
  12. * and set owner.
  13. */
  14. Thread current = Thread.currentThread();
  15. int c = getState();
  16. int w = exclusiveCount(c);
  17. if (c != 0) {
  18. // (Note: if c != 0 and w == 0 then shared count != 0)
  19. //这里也给了注释,如果 c!=0 并且 w=0,那么说明还有读的线程在持有锁
  20. if (w == 0 || current != getExclusiveOwnerThread())
  21. return false;
  22. //超过最大数,就抛出错误
  23. if (w + exclusiveCount(acquires) > MAX_COUNT)
  24. throw new Error("Maximum lock count exceeded");
  25. // Reentrant acquire
  26. //能够执行到这里,说明没有读线程持有锁,并且是当前线程持有了锁。就直接更新 state,而不用通过 CAS来更新。
  27. setState(c + acquires);
  28. return true;
  29. }
  30. //执行到这里,说明其它写线程持有锁,那么当前写线程就要争夺锁。
  31. //writerShouldBlock 是由 Sync 的子类 FairSync 和 NonfairSync 来实现。它们两个的实现方式不同。
  32. if (writerShouldBlock() ||
  33. !compareAndSetState(c, c + acquires))
  34. return false;//争夺锁失败
  35. //争夺锁成功,将当前的线程设置到独占线程的标识上。
  36. setExclusiveOwnerThread(current);
  37. return true;
  38. }

和 ReentrantLock 不同的是,这里的独占锁获取锁的时候,由于共享锁的存在,对 state 的处理也要复杂一点。要先判断是否还有共享锁持有线程。再去争夺锁。

  1. //ReentrantReadWriteLock.FairSync.java
  2. final boolean writerShouldBlock() {
  3. return hasQueuedPredecessors();
  4. }
  5. //ReentrantReadWriteLock.NonfairSync.java
  6. final boolean writerShouldBlock() {
  7. return false; // writers can always barge
  8. }

从上面也可以看出,公平锁、非公平锁的判断逻辑和 ReentrantLock 是一样的。
公平锁要先看当前队列是否还有等待的线程,如果有,自己就争夺锁失败,然后老老实实的去排队。
非公平锁则直接去争夺。

写饥饿

ReentrantReadWriteLock实现了读写锁,但它有一个小弊端,就是在“写”操作的时候,其它线程不能写也不能读。我们称这种现象为“写饥饿”

5.CountDownLatch

多线程-5.7 锁-CountDownLatch源码分析

6.CyclicBarrier

多线程-5.8 锁-CyclicBarrier源码分析

7.Semaphore

多线程-5.9 锁-Semaphore源码分析

8.StampedLock

StampedLock类是在Java 8 才发布的,也是Doug Lea大神所写,有人号称它为锁的性能之王。它没有实现Lock接口和ReadWriteLock接口,但它其实是实现了“读写锁”的功能,并且性能比ReentrantReadWriteLock更高。StampedLock还把读锁分为了“乐观读锁”和“悲观读锁”两种。
前面提到了ReentrantReadWriteLock会发生“写饥饿”的现象,但StampedLock不会。它是怎么做到的呢?它的核心思想在于,在读的时候如果发生了写,应该通过重试的方式来获取新的值,而不应该阻塞写操作。这种模式也就是典型的无锁编程思想,和CAS自旋的思想一样。这种操作方式决定了StampedLock在读线程非常多而写线程非常少的场景下非常适用,同时还避免了写饥饿情况的发生。

参考文献

多线程系列文章目录
深入浅出Java多线程