ReentrantLock 1.8解析

ReentrantLock , 可重入锁, 和 synchronized 有着相同的内存语义

比较下这俩者之间的相同以及区别

相同点:

俩者有着相同的内存语义,

1、当线程获取锁时,JMM会把线程对应的本地内存置为无效,然后临界区的代码从主存中读入共享变量到工作内存。
2、 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。

区别在于,ReentrantLock是应用层面, 是通过volatile来实现同样的语义

不同点:

synchronized是jvm内置锁,ReentrantLock是应用层面实现的锁,所以ReentrantLock在运用方面会更灵活

1、ReentrantLock提供了 锁查询(tryLock)、锁等待 等方法,可以有效的防止死锁,synchronized只能阻塞了

2、ReentrantLock提供了可以中断的加锁机制(lockInterruptibly),synchronized不响应中断

3、ReentrantLock提供了公平锁模式,synchronized只有非公平锁

4、ReentrantLock支持更加灵活的同步代码块,但是这也导致了一定的风险,一定要保证在finally中处理锁释放,synchronized则没有这方面担忧,是由jvm控制

性能方面:

这一块很多人有误解,认为ReentrantLock一定比synchronized要优秀,实际上在Java1.6之前确实是这样,不过从Java1.6开始,synchronized做了很多性能优化,已经不比ReentrantLock差.
而且随着jvm的发展和完善,synchronized的性能会越来越好,毕竟它是jvm内置的锁,在扩展优化方面有着优势.
java 1.8版的ConcurrentHashMap就是用cas和synchronized完成的

如何选择

1、大部分情况下,如果ReentrantLock和synchronized都能满足需求,优先选择synchronized.
2、如果有锁嵌套的情况下,可以考虑使用ReentrantLock的锁查询、锁等待等机制,防止死锁
3、特殊场景需要用到公平锁,考虑使用ReentrantLock
4、要使用多个条件队列的情况下,考虑使用ReentrantLock的condition

接下来我们分析下ReentrantLock的源码,先看下部分类图结构

摘自java 并发编程的艺术 3.5.3小节

从图中可以看出ReentrantLock是用内部类(Sync)继承了AQS, 然后在这个基础上实现了俩个子类, NonfairSync(非公平锁),FairSync(公平锁)

因为是继承了AQS,所以ReentrantLock只需要在这个基础上完善tryAcquire、tryRelease方法即可,接下来以非公平锁为例,看下源码部分

		protected final boolean tryAcquire(int acquires) {
		    return nonfairTryAcquire(acquires);
		}


		//非公平锁实现具体的tryAcquire逻辑,因为是独占锁,这里的acquires传入1
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //获取同步状态state,如果为0说明此时没有竞争,可以尝试获取同步状态
            int c = getState();
            if (c == 0) {
                //通过cas 将状态设置为tryAcquire
                if (compareAndSetState(0, acquires)) {
                    //因为是独占锁,所以成功后,记录下获取同步状态的线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果state不为0,说明已经有线程获取了同步状态,检查是否是当前线程获取
            else if (current == getExclusiveOwnerThread()) {
                //如果是的话,则获取同步状态,并且state+1,表示重入的次数
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

以上是尝试获取同步状态的方法,成功返回true,失败返回false, 如果返回false,AQS将会将该线程放入锁同步队列中阻塞等待(详情看这里AbstractQueuedSynchronizer解析)

		//释放同步状态,因为是独占锁这里的releases为1
        //因为是可重入锁,这里state表示重入次数,释放一次state-1
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            //如果获取同步状态的线程不是当前线程,则抛IllegalMonitorStateException
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //如果state为0说明同步状态已完全释放
            if (c == 0) {
                free = true;
                //在这里将之前记录获取同步状态的线程清除
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

上面是尝试释放同步状态的方法,成功返回true,失败返回false,如果为true,AQS将会唤醒同步锁队列中的线程,继续竞争同步状态

ReentrantLock支持公平锁,看下这部分实现逻辑

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        //该方法在类FairSync下,逻辑和非公平锁基本一致
        //唯一的区别是在竞争同步状态之前,需要判断队列中是否有其它正在等待的线程
        //如果有,那么只能放弃竞争老老实实的进入队列排在最后面
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //重点在这里,多了一个hasQueuedPredecessors方法
                //判断同步锁队列中是否还有其他正在等待的节点
                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;
        }
    }
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值