5、锁内存语义的实现

锁内存语义的实现

在ReentrantLock中,调用lock()方法获取锁;调用unlock()方法释放锁。

ReentrantLock的实现依赖于Java同步器框架AbstractQueuedSynchronizer(本文简称之为AQS)。AQS使用一个整型的volatile变量(命名为state)来维护同步状态,这个volatile变量是ReentrantLock内存语义实现的关键。

ReentrantLock分为公平锁和非公平锁.我们首先分析公平锁。

使用公平锁时,加锁方法lock()调用轨迹如下。

1)ReentrantLock:lock()。

2)FairSync:lock()。

3)AbstractQueuedSynchronizer:acquire(int arg)。

4)ReentrantLock:tryAcquire(int acquires)。

在第4步真正开始加锁,下面是该方法的源代码。

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();    // 获取锁的开始,首先读volatile变量state
    if (c == 0) {
        if (isFirst(current) &&
            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;
}

从上面源代码中我们可以看出,加锁方法首先读volatile变量state。

在使用公平锁时,解锁方法unlock()调用轨迹如下。

1)ReentrantLock:unlock()。

2)AbstractQueuedSynchronizer:release(int arg)。

3)Sync:tryRelease(int releases)。

在第3步真正开始释放锁,下面是该方法的源代码。

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);     // 释放锁的最后,写volatile变量state
       return free;
}

从上面的源代码可以看出,在释放锁的最后写volatile变量state。

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

现在我们来分析非公平锁的内存语义的实现。非公平锁的释放和公平锁完全一样,所以这里仅仅分析非公平锁的获取。使用非公平锁时,加锁方法lock()调用轨迹如下。

1)ReentrantLock:lock()。

2)NonfairSync:lock()。

3)AbstractQueuedSynchronizer:compareAndSetState(int expect,int update)。

在第3步真正开始加锁,下面是该方法的源代码。

protected final boolean compareAndSetState(int expect, int update) {
       return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

该方法以原子操作的方式更新state变量,本文把Java的compareAndSet()方法调用简称为CAS。JDK文档对该方法的说明如下:如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的更新值。此操作具有volatile读和写的内存语义。这里我们分别从编译器和处理器的角度来分析,CAS如何同时具有volatile读和volatile写的内存语义。

前文我们提到过,编译器不会对volatile读与volatile读后面的任意内存操作重排序;编译器不会对volatile写与volatile写前面的任意内存操作重排序。组合这两个条件,意味着为了同时实现volatile读和volatile写的内存语义,编译器不能对CAS与CAS前面和后面的任意内存操作重排序。

现在对公平锁和非公平锁的内存语义做个总结。

·公平锁和非公平锁释放时,最后都要写一个volatile变量state。

·公平锁获取时,首先会去读volatile变量。

·非公平锁获取时,首先会用CAS更新volatile变量,这个操作同时具有volatile读和volatile写的内存语义。

从本文对ReentrantLock的分析可以看出,锁释放-获取的内存语义的实现至少有下面两种方式。

1)利用volatile变量的写-读所具有的内存语义。

2)利用CAS所附带的volatile读和volatile写的内存语义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值