java.util.concurrent.locks之ReentrantLock

一、ReentrantLock概述

ReentrantLock是API层面的互斥锁(lock()/unlock()方法配合try/finally配合使用);
ReentrantLock高级功能:等待可中断、可实现公平锁,以及锁可以绑定多个条件;
ReentrantLock的实现依赖于Java同步器框架AbstractQueuedSynchronizer(简称之为AQS),其中AQS使用一个整型的volatile变量(命名为state)来维护同步状态,马上我们会看到,这个volatile变量是ReentrantLock内存语义实现的关键。

abstract static class Sync extends AbstractQueuedSynchronizer {... ...}

二、ReentrantLock和Synchronized的区别

ReentrantLock是API层面的互斥锁;
Synchronized是原生语言支出的互斥锁,是可重入锁;
ReentrantLock可以分为公平锁和非公平锁,底层由乐观策略解决阻塞同步的问题,也被称为乐观锁;
Synchronized是只要不去做正确的同步措施就会出错,无论数据共享是否真的存在竞争,因此被称为悲观锁;

三、ReentrantLock的实现

ReentrantLock的公平锁加锁过程:

ReentrantLock:lock()
FairSync:lock()
AbstractQueuedSynchronizer:acquire(int arg)
ReentrantLock:tryAcquire(int acquires)

在上面的自上而下的顺序中,只有偶在tryAcquire(int acquire)这个才是真正的开始加锁:

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

ReentrantLock公平锁解锁过程:

ReentrantLock:unlock()
AbstractQueuedSynchronizer:release(int arg)
Sync:tryRelease(int releases)

在上面的自上而下的顺序中,只有偶在tryRelease(int release)这个才是真正的开始加锁:

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

ReentrantLock非公平锁的释放和公平锁完全一样,所以这里仅仅分析非公平锁的获取:

ReentrantLock:lock()
NonfairSync:lock()
AbstractQueuedSynchronizer:compareAndSetState(int expect,int update)

ReentrantLock非公平锁加锁原理:

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

在这里我们看到使用了compareAndSet这个方法来完成volatile的states变量这个状态的更新;
我们把compareAndSet()方法称为CAS,也就叫比较替换法,在锁种被称为乐观锁;
JDK对compareAndSet()描述是:如果当前状态值等于预期值,则以原子方式将同步状态,则设置为给定的更新值;

四、CAS实现原子操作的三大问题

1、ABA问题
​ 问题产生原因:如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。
​ 解决办法:使用版本号:1A=>2B=>3C
​ java1.5JDK的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。

public boolean compareAndSet(
	V expectedReference, // 预期引用
	V newReference, // 更新后的引用
	int expectedStamp, // 预期标志
	int newStamp // 更新后的标志
)

2、循环时间长,开销大
​ 问题产生的原因:CAS自选锁在长时间执行不成功的话会一直执行,对于内存消耗非常大。
​ 解决办法:使用pause指令。
​ 1)、它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间;
​ 2)、它可以避免在退出循环的时候因内存顺序冲突(Memory Order Violation)而引起CPU流水线被清空(CPU Pipeline Flush),从而提高CPU的执行效率。
​3、只能保证一个共享变量的原子操作
​ 问题产生原因:多个共享变量操作时,循环CAS就无法保证操作的原子性
​ 解决办法:加锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值