当前位置: 代码迷 >> 综合 >> Java基础- ReentrantLock
  详细解决方案

Java基础- ReentrantLock

热度:63   发布时间:2023-12-15 12:58:39.0
ReentrantLock概述

ReentrantLock,可重入锁,是一种递归无阻塞的同步机制。它可以等同于 synchronized 的使用,但是 ReentrantLock 提供了比 synchronized 更强大、灵活的锁机制,可以减少死锁发生的概率
源码:

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;abstract static class Sync extends AbstractQueuedSynchronizer {
    /*** Performs {@link Lock#lock}. The main reason for subclassing* is to allow fast path for nonfair version.*/abstract void lock();/*** Performs non-fair tryLock. tryAcquire is implemented in* subclasses, but both need nonfair try for trylock method.*/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) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}protected final boolean tryRelease(int releases) {
    int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;if (c == 0) {
    free = true;setExclusiveOwnerThread(null);}setState(c);return free;}}//默认非公平锁public ReentrantLock() {
    sync = new NonfairSync();}//fair为false时,采用公平锁策略public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();}public void lock() {
    sync.lock();}public void unlock() {
        sync.release(1);}public Condition newCondition() {
        return sync.newCondition();}...
}
  • 从源代码可以看出lock和unlock方法委托给同步器完成。
ReentrantLock使用
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
    while(条件判断表达式) {
    condition.wait();}// 处理逻辑
} finally {
    lock.unlock();
}
  • 需要显示的获取锁,并在finally块中显示的释放锁,目的是保证在获取到锁之后,最终能够被释放。

非公平锁实现
ReentrantLock默认是非公平锁,在非公平锁中,每当线程执行lock方法时,都尝试利用CAS把state从0设置为1。
ReentrantLock是如何实现锁的非公平性呢

  • 当线程要获取锁时,先通过两次 CAS 操作去抢锁,如果没抢到,当前线程再加入到队列中等待唤醒。

公平锁实现
在公平锁中,每当线程执行lock方法时,如果同步器的队列中有线程在等待,则直接加入到队列中。
场景分析:

  • 持有锁的线程A正在running,对列中有线程BCDEF被挂起并等待被唤醒;
  • 线程G执行lock,队列中有线程BCDEF在等待,线程G直接加入到队列的对尾。
  • 所以每个线程获取锁的过程是公平的,等待时间最长的会最先被唤醒获取锁。

公平锁和非公平锁只有两处不同:

  • 非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。
  • 非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。
  • 相对来说,非公平锁会有更好的性能,因为它的吞吐量比较大。当然,非公平锁让获取锁的时间变得更加不确定,可能会导致在阻塞队列中的线程长期处于饥饿状态。

重入锁实现
重入锁,即线程可以重复获取已经持有的锁。在非公平和公平锁中,都对重入锁进行了实现。

条件变量Condition
条件变量很大一个程度上是为了解决Object.wait/notify/notifyAll难以使用的问题

public class ConditionObject implements Condition, java.io.Serializable {
    /** First node of condition queue. */private transient Node firstWaiter;/** Last node of condition queue. */private transient Node lastWaiter;public final void signal() {
    }public final void signalAll() {
    }public final void awaitUninterruptibly() {
    }  public final void await() throws InterruptedException {
    }
}
  • Synchronized中,所有的线程都在同一个object的条件队列上等待。而ReentrantLock中,每个condition都维护了一个条件队列。
  • 每一个Lock可以有任意数据的Condition对象,Condition是与Lock绑定的,所以就有Lock的公平性特性:如果是公平锁,线程为按照FIFO的顺序从Condition.await中释放,如果是非公平锁,那么后续的锁竞争就不保证FIFO顺序了。
  • Condition接口定义的方法,await对应于Object.wait,signal对应于Object.notify,signalAll对应于Object.notifyAll。特别说明的是Condition的接口改变名称就是为了避免与Object中的wait/notify/notifyAll的语义和使用上混淆。
补充

ReentrantLock 和 ReentrantReadWriteLock
ReentrantLock 是独占锁, ReentrantReadWriteLock 读写锁,可以保证多个线程可以同时读,所以在读操作远大于写操作的时候,读写锁就非常有用了。

  相关解决方案