并发编程之锁(三)--ReentrantLock

前言

上一篇中已经分析了关键的AQS抽象队列同步器,下面我们来看一下使用AQS来实现的可重入独占锁ReentrantLock。
ReentrantLock是可重入的独占锁,同时只有一个线程可以获取该锁,下面我们来看下其类图:


ReentrantLock结构图

Sync抽象类

Sync是ReentrantLock的静态内部类,继承自AbstractQueuedSynchronizer。它使用AQS的state字段来表示当前锁的持有数量,从而实现可重入的特性。

  • #lock()
/**
 * Performs {@link Lock#lock}. The main reason for subclassing
 * is to allow fast path for nonfair version.
 */
abstract void lock();

允许子类实现快速获得非公平锁的逻辑。

  • #nonfairTryAcquire(int acquires)
final boolean nonfairTryAcquire(int acquires) {
    //1.当前线程
    final Thread current = Thread.currentThread();
    //2.获取同步状态
    int c = getState();
    //3.state==0表示当前锁处于空闲状态
    if (c == 0) {
    //3.1 通过CAS获取锁,获取成功后设置为当前线程所有
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //4.判断锁持有的线程是否为当前线程,可重入
    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;
}
  • #tryRelease(int releases)
protected final boolean tryRelease(int releases) {
    //1. 剩余可重入数量
    int c = getState() - releases;
    //2. 持有锁的线程不是当前线程,直接抛出异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //3. c == 0 表示已经释放完全了,其他线程可以获取同步状态了
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
  • 其他的方法
protected final boolean isHeldExclusively() {
    // While we must in general read state before owner,
    // we don't need to do so to check if current thread is owner
    return getExclusiveOwnerThread() == Thread.currentThread();
}

final ConditionObject newCondition() {
    return new ConditionObject();
}

// Methods relayed from outer class

final Thread getOwner() {
    return getState() == 0 ? null : getExclusiveOwnerThread();
}

final int getHoldCount() {
    return isHeldExclusively() ? getState() : 0;
}

final boolean isLocked() {
    return getState() != 0;
}
/**
 * 自定义序列化逻辑
 */
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();
    setState(0); // reset to unlocked state
}

Sync实现类

  • NonfairSync,非公平锁实现类
final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
  • FairSync,公平锁实现类
final void lock() {
    acquire(1);
}

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
  • 区别
    非公平锁和公平锁获取同步状态的过程唯一的区别就在于,公平锁在获取同步状态时多了一个限制条件#hasQueuedPredecessors()方法,是否有前序节点,即自己不是首个等待获取同步状态的节点。代码如下:
public final boolean hasQueuedPredecessors() {
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    //头节点 != 尾节点
    //同步队列第一个节点不为null
    //当前线程是同步队列第一个节点
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

这里主要是判断当前线程是否位于 CLH 同步队列中的第一个。如果是则返回 true ,否则返回 false 。

ReentrantLock

  • 构造方法
/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
public ReentrantLock() {
    sync = new NonfairSync();
}

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

默认是非公平锁,但是可以通过构造方法设置。

  • lock、trylock、unlock

public void lock() {
    sync.lock();
}

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

public void unlock() {
    sync.release(1);
}

总结

ReentrantLock 与 synchronized 的区别

  • 与synchronized相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候,锁投票。
  • ReentrantLock还提供了条件Condition,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock 更加适合。
  • ReentrantLock提供了可轮询的锁请求。它会尝试着去获取锁,如果成功则继续,否则可以等到下次运行时处理,而synchronized则一旦进入锁请求要么成功要么阻塞,所以相比synchronized而言,ReentrantLock会不容易产生死锁些。
  • ReentrantLock 支持更加灵活的同步代码块,但是使用 synchronized 时,只能在同一个 synchronized块结构中获取和释放。注意,ReentrantLock 的锁释放一定要在 finally 中处理,否则可能会产生严重的后果。
  • ReentrantLock 支持中断处理,且性能较 synchronized 会好些。

你可能感兴趣的:(并发编程之锁(三)--ReentrantLock)