目录
1. ReentrantLock & ReentrantReadWriteLock使用场景
ReentrantLock 和 synchronized的区别
1. ReentrantLock & ReentrantReadWriteLock使用场景
ReentrantLock 和 synchronized的区别
- 用法不同
-
synchronized 可用来修饰普通方法、静态方法和代码块,而 ReentrantLock 只能用在代码块上。
-
-
释放方式不同
-
synchronized可以自己释放锁, ReentrantLock需要手动
-
-
底层实现方式不同
-
synchronized作用在JVM上, ReentrantLock是基于java代码实现的
-
-
锁类型不同
-
synchronized只支持非公平锁, ReentrantLock支持公平锁 & 非公平锁
-
-
响应中断不同
-
ReentrantLock可以支持lockInterruptibly相应中断, 如果有死锁,可以直接报Expection
-
ReentrantReadWriteLock
现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。
锁的大概方法
解释, lockInterruptibly , 如果手动调用线程, 把线程interruptibly,那么就会立刻给出中断信息
2. Sync详解
-
有FairSync 和 NonFairSync
-
FairSync
-
FairSync --- 获取锁
-
// 尝试获取锁 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); // 获取node节点的状态, 是AQS配合使用的 // AQS中用state表示这个锁有没有人获取 int c = getState(); // 0表示没有人获取锁 if (c == 0) { // 这个就是公平锁的实现, 去看之前是否有等待的锁 if (!hasQueuedPredecessors() && // 设置状态, 把state设置成获取多少的数(上游传递保证冲入等) compareAndSetState(0, acquires)) { // 设置目前占用这个锁的线程是什么 setExclusiveOwnerThread(current); return true; } } // 当状态不是0的时候,说明是有人 // 看下现在独占这个锁的线程, 如果是本线程,那么就是重入锁的设置 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } // AQS中的方法,之前是否有节点在等待锁 public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
-
-
NonFairSync -- 获取锁
-
final void lock() { // 设置状态, 如果状态成功,那么说明就可以获取到锁 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else // AQS中的方法, 会调用下面的nonfairTryAcquire acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } // 非公平获取锁 final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // 如果没有人有锁 if (c == 0) { // 尝试获取 if (compareAndSetState(0, acquires)) { // 设置独占线程,用于重入 setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
-
3. ReentrantReadWriteLock源码解析
-
类属性
-
bstract static class Sync extends AbstractQueuedSynchronizer { // 版本序列号 private static final long serialVersionUID = 6317671515068378041L; // 高16位为读锁,低16位为写锁 static final int SHARED_SHIFT = 16; // 读锁单位 static final int SHARED_UNIT = (1 << SHARED_SHIFT); // 读锁最大数量 static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; // 写锁最大数量 static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 本地线程计数器 private transient ThreadLocalHoldCounter readHolds; // 缓存的计数器 private transient HoldCounter cachedHoldCounter; // 第一个读线程 private transient Thread firstReader = null; // 第一个读线程的计数 private transient int firstReaderHoldCount; }
-
-
读写状态是如何设计的
- 将AQS中的state字段 (线程状态字段, 分成了高16位和低16位)用一个整形,维护了读锁和写锁
- 读写锁对于同步状态的实现是在一个整形变量上通过“按位切割使用”:将变量切割成两部分,高16位表示读,低16位表示写。
- 那么如何判断是否持有了写锁 / 读锁
- 用state >> 16获取 高位16读 低位16写
-
基本读写锁结构
-
// 读锁标志 private final ReentrantReadWriteLock.ReadLock readerLock; // 写锁标志 private final ReentrantReadWriteLock.WriteLock writerLock; public static class ReadLock implements Lock, java.io.Serializable { private static final long serialVersionUID = -5992448646407690164L; private final Sync sync; protected ReadLock(ReentrantReadWriteLock lock) { sync = lock.sync; } // 获取锁, public void lock() { sync.acquireShared(1); } // 建立一个可以被中断的锁 public void lockInterruptibly() throws InterruptedException { sync.acquireSharedInterruptibly(1); } // 会立刻返回结果,不会等待 public boolean tryLock() { return sync.tryReadLock(); } public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } // 释放锁 public void unlock() { sync.releaseShared(1); } } // 写锁 public static class WriteLock implements Lock, java.io.Serializable { private static final long serialVersionUID = -4992448646407690164L; private final Sync sync; protected WriteLock(ReentrantReadWriteLock lock) { sync = lock.sync; } public void lock() { sync.acquire(1); } public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } public boolean tryLock( ) { return sync.tryWriteLock(); } public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } public void unlock() { sync.release(1); } }
-
- 代码所示
- 读锁 : acquireShared acquireSharedInterruptibly. releaseShared
- 写锁 : acquire. acquireInterruptibly. release
-
写锁的获取和释放
-
写锁获取
-
protected final boolean tryAcquire(int acquires) { // 获取当前线程 Thread current = Thread.currentThread(); int c = getState(); // 获取写锁个数 int w = exclusiveCount(c); // 状态是不可以获取 if (c != 0) { // (Note: if c != 0 and w == 0 then shared count != 0) // 查看冲入 if (w == 0 || current != getExclusiveOwnerThread()) return false; // 冲入不能超过16位 if (w + exclusiveCount(acquires) > MAX_COUNT) throw new Error("Maximum lock count exceeded"); // 设置状态 setState(c + acquires); return true; } // 可以获取锁 if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) return false; // 设置当前获取到锁的线程 setExclusiveOwnerThread(current); return true; } // 非公平锁。永远都可以获取 final boolean writerShouldBlock() { return false; // writers can always barge } // 是否前面有等待的节点 fairSync final boolean writerShouldBlock() { return hasQueuedPredecessors(); }
-
-
-
写锁的释放 -- release
-
public final boolean release(int arg) { // 释放锁 if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } protected final boolean tryRelease(int releases) { // 如果释放锁的不是ownerThread设置的线程, 直接gg if (!isHeldExclusively()) throw new IllegalMonitorStateException(); int nextc = getState() - releases; // 如果重入的状态已经是0了 boolean free = exclusiveCount(nextc) == 0; // 释放锁 if (free) setExclusiveOwnerThread(null); setState(nextc); return free; }
-
读锁的获取和释放
-
读锁的获取
protected final int tryAcquireShared(int unused) { Thread current = Thread.currentThread(); int c = getState(); // 如果独占锁的数目大于0, 并且不是当前线程 ,有线程占有了锁 if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) return -1; int r = sharedCount(c); // 读锁是不是需要等待 if (!readerShouldBlock() && r < MAX_COUNT && compareAndSetState(c, c + SHARED_UNIT)) { if (r == 0) { // 如果能获取读锁了, 那么就设置一下当前的count firstReader = current; firstReaderHoldCount = 1; } else if (firstReader == current) { firstReaderHoldCount++; } else { HoldCounter rh = cachedHoldCounter; if (rh == null || rh.tid != getThreadId(current)) cachedHoldCounter = rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); rh.count++; } return 1; } return fullTryAcquireShared(current); } // 说明:在tryAcquireShared函数中,如果下列三个条件不满足(读线程是否应该被阻塞、小于最大值、比较设置成功)则会进行fullTryAcquireShared函数中,它用来保证相关操作可以成功。其逻辑与tryAcquireShared逻辑类似,不再累赘。 final int fullTryAcquireShared(Thread current) { HoldCounter rh = null; for (;;) { int c = getState(); // 写锁的个数 if (exclusiveCount(c) != 0) { if (getExclusiveOwnerThread() != current) return -1; // 读锁是否应该block, 和公平 & 非公平锁有关系 } else if (readerShouldBlock()) { if (firstReader == current) { // assert firstReaderHoldCount > 0; } else { if (rh == null) { rh = cachedHoldCounter; if (rh == null || rh.tid != getThreadId(current)) { rh = readHolds.get(); if (rh.count == 0) readHolds.remove(); } } if (rh.count == 0) return -1; } } if (sharedCount(c) == MAX_COUNT) throw new Error("Maximum lock count exceeded"); if (compareAndSetState(c, c + SHARED_UNIT)) { if (sharedCount(c) == 0) { firstReader = current; firstReaderHoldCount = 1; } else if (firstReader == current) { firstReaderHoldCount++; } else { if (rh == null) rh = cachedHoldCounter; if (rh == null || rh.tid != getThreadId(current)) rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); rh.count++; cachedHoldCounter = rh; // cache for release } return 1; } } } // 当前获取的节点, 头节点是不是共享readerShouldBlock 的非公平实现 final boolean apparentlyFirstQueuedIsExclusive() { Node h, s; return (h = head) != null && (s = h.next) != null && !s.isShared() && s.thread != null; }
-
读锁的释放
-
public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; } // 尝试释放读锁 protected final boolean tryReleaseShared(int unused) { // 获取当前线程 Thread current = Thread.currentThread(); if (firstReader == current) { // 当前线程为第一个读线程 // assert firstReaderHoldCount > 0; if (firstReaderHoldCount == 1) // 读线程占用的资源数为1 firstReader = null; else // 减少占用的资源 firstReaderHoldCount--; } else { // 当前线程不为第一个读线程 // 获取缓存的计数器 HoldCounter rh = cachedHoldCounter; // 计数器为空或者计数器的tid不为当前正在运行的线程的tid if (rh == null || rh.tid != getThreadId(current)) // 获取当前线程对应的计数器 rh = readHolds.get(); // 获取计数 int count = rh.count; if (count <= 1) { // 计数小于等于1 // 移除 readHolds.remove(); if (count <= 0) // 计数小于等于0,抛出异常 throw unmatchedUnlockException(); } // 减少计数 --rh.count; } for (;;) { // 无限循环 // 获取状态 int c = getState(); // 获取状态 int nextc = c - SHARED_UNIT; if (compareAndSetState(c, nextc)) // 比较并进行设置 // Releasing the read lock has no effect on readers, // but it may allow waiting writers to proceed if // both read and write locks are now free. return nextc == 0; } } // 这个是每个线程进来, 都想去获取锁, 然后通过setHeadAndPropagate每次获取到读锁之后,都会更新head,这样的话, 就是acquire和release方法配合使用,风暴遍历,把后面的读线程都唤醒 private void doAcquireShared(int arg) { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { boolean interrupted = false; for (;;) { // 找前面的节点 final Node p = node.predecessor(); if (p == head) { // 释放锁 int r = tryAcquireShared(arg); if (r >= 0) { // 设置头,保证上面的 setHeadAndPropagate(node, r); p.next = null; // help GC if (interrupted) selfInterrupt(); failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } } private void doReleaseShared() { for (;;) { Node h = head; if (h != null && h != tail) { int ws = h.waitStatus; //在队列中的节点对应的线程阻塞之前,将前驱节点的waitStatus状态设置为SIGNAL //所以这块ws==0,其实是当前线程通过第一次循环将状态设置为了0, //第二次循环进入的时候头节点还没有被改变 //cas操作失败的话会直接continue,为什么会失败, //可能是唤醒得其他节点在唤醒后续节点的时候已经进行了修改 //修改失败则代表头节点已经修改,则进入下一次循环 if (ws == Node.SIGNAL) { if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; // loop to recheck cases unparkSuccessor(h); } else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed break; } }
所以流程是
读锁是每次都在唤醒的
-
调用上面的tryAcquired的时候 会在循环调用 setHeadAndPropagate ,在这个方法里面,会不断的去设置队列的head, 然后在h==head的时候,就不会结束,就会唤醒下一个节点
-
-
-