java并发- ReentrantLock

ReentrantLock

一、源码

类图

  1. ReentrantLock实现了Lock接口,加锁和解锁都需要显式写出,注意一定要在适当时候unlock。
    ReentrantLock的类图

公平锁和非公平锁

  • 公平锁:线程获取锁的顺序和调用lock的顺序一样,FIFO;
  • 非公平锁:线程获取锁的顺序和调用lock的顺序无关,全凭运气。

ps:
ReentrantLock默认使用非公平锁是基于性能考虑,公平锁为了保证线程规规矩矩地排队,需要增加阻塞和唤醒的时间开销。如果直接插队获取非公平锁,跳过了对队列的处理,速度会更快。

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

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

ReentrantLock的内部类Sync继承了AQS,分为公平锁FairSync和非公平锁NonfairSync。
ReentrantLock的公平锁和非公平锁都是基于AbstractQueuedSynchronizer(AQS)实现的。ReentrantLock使用的是AQS的排他锁模式,由于AQS除了排他锁模式还有共享锁模式,本文仅对ReentrantLock涉及到的排他锁模式部分的内容进行介绍,关于共享锁模式的部分会在 CountDownLatch 源码浅析一文中介绍。

公平锁和非公平锁获取锁的对比

公平锁加锁调用顺序
  1. ReentrantLock:lock()
  2. FairSync:lock()
  3. AbstractQueuedSynchronizer:acquire(int acquires)
  4. FairSync:tryAcquire(int acquires)
    其中第四步才开始加锁,源码如下:
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;
        }
        
非公平锁加锁调用顺序
  1. ReentrantLock:lock()
  2. NonfairSync:lock()
  3. Sync:nonfairTryAcquire(int acquires)
  4. AbstractQueuedSynchronizer:compareAndSetState(int expect, int update)

第三步源码

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;
        }
对比
  1. 可以看出公平锁的加锁多了一个判断!hasQueuedPredecessors(), 即加入了同步队列中当前节点是否有前驱节点的判断,如果该方法返回true,则表示有线程比当前线程更早地请求获取锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。
  2. 加锁最终都是调用的AQS的compareAndSetState方法
protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
// 设置锁的持有者为当前线程
protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
 }

公平锁和非公平锁的解锁

解锁步骤是一样的

调用顺序
  1. ReentrantLock:unlock()
  2. AbstractQueuedSynchronizer:release(int acquires)
  3. Sync:tryRelease(int acquires)

其中acquires=1,看下tryRelease方法:

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;
        }
分析

公平锁在释放锁的最后写volatile变量state;在获取锁时首先读这个volatile变量。根据volatile的happens-before规则,释放锁的线程在写volatile变量之前可见的共享变量,在获取锁的线程读取同一个volatile变量后将立即变的对获取锁的线程可见

二、原理分析

CAS[原子性]+volatile[可见性]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值