美文网首页
Java 并发编程之重入锁

Java 并发编程之重入锁

作者: Tinyspot | 来源:发表于2023-04-06 21:05 被阅读0次

1. ReentrantLock

ReentrantLock是可重入的独占锁,同时只能有一个线程可以获取该锁,其他获取该锁的线程会被阻塞而被放入该锁的AQS阻塞队列里面

    @Test
    public void reentrantLock() {
        ReentrantLock fairLock = new ReentrantLock(true);

        ReentrantLock nonFairLock = new ReentrantLock();
        ReentrantLock nonFairLock2 = new ReentrantLock(false);
    }

特性:

  1. 可重入
  2. 可中断
  3. 可设置超时时间
  4. 可以设置为公平锁(先进先出)
  5. 支持多个条件变量
  • synchronized 只有一个 waitSet
  • reentrantLock 可以有多个 waitSet

2. 实战

2.1 synchronized 与 reentrantLock

  • synchronized 是在关键字级别保护临界区
  • reentrantLock 是在对象级别保护临界区
    try {
        // 临界区
    } final {
        reentrantLock.unlock();
    }

3. 源码分析

  • ReentrantLock 实现 Lock 接口,有 3 个内部类:Sync, NonfairSync, FairSync
  • Sync 类继承自抽象类 AbstractQueuedSynchronizer(AQS),它的子类 NonfairSync 和 FairSync 分别实现了获取锁的非公平与公平策略
/**
 * 看注释...
 * A ReentrantLock is owned by the thread last successfully locking, but not yet unlocking it
 */
public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;

    abstract static class Sync extends AbstractQueuedSynchronizer {
    }

    static final class NonfairSync extends Sync {

    }

    static final class FairSync extends Sync {

    }
}

3.1 成员变量 sync

private final Sync sync;
变量 sync 用来指向 Sync 子类,也就是FairSync或者NonfairSync,多态的父类引用指向子类,具体Sycn指向哪个子类,看构造方法

    public ReentrantLock() {
        sync = new NonfairSync();
    }

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

3.2 内部类 Sync

Sync 重写的方法 tryRelease, tryAcquire(子类重写),ReentrantLock实现的是AQS的独占模式,也就是独占锁,这个锁是悲观锁

image.png

3.3 lock()

ReentrantLock 的 lock() 委托给 sync 类

public class ReentrantLock implements Lock, java.io.Serializable {
    public void lock() {
        sync.lock();
    }

    abstract static class Sync extends AbstractQueuedSynchronizer {
        abstract void lock();
    }

    static final class NonfairSync extends Sync {
        // 非公平锁实现
        final void lock() {
            // boolean compareAndSetState(int expect, int update)
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 调用 AQS 的 acquire()
                acquire(1);
        }
    }

    static final class FairSync extends Sync {
        // 公平锁实现
        final void lock() {
            acquire(1);
        }
    }
}

分析非公平锁
lock() 调用 CAS方法设置state的值
setExclusiveOwnerThread设置该锁持有者是当前线程

AQS 的 acquire()

public final void acquire(int arg) {
    // ReentrantLock 重写 tryAcquire()
    if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), arg))
        selfInterrupt();
}

扩展:
AQS 的 state状态值表示线程获取该锁的可重入次数,
state 默认为0,表示当前锁没有被任何线程持有
当一个线程第一次获取该锁时会尝试使用CAS设置state的值为1,如果CAS成功则当前线程获取了该锁,然后记录该锁的持有者为当前线程。在该线程没有释放锁的情况下第二次获取该锁后,状态值被设置为2,这就是可重入次数。在该线程释放该锁时,会尝试使用CAS让状态值减1,如果减1后状态值为0,则当前线程释放该锁

相关文章

网友评论

      本文标题:Java 并发编程之重入锁

      本文链接:https://www.haomeiwen.com/subject/jpiuddtx.html