java.util.concurrent学习(九) Lock,ReentrantLock

21 篇文章 0 订阅

 

Lock框架

Lock

Lock是java.util.concurrent.locks包提供的一个锁操作接口,相比于synchronized只能定义实例域和Class域的代码块,该接口提供了更灵活的方法,在代码层面提供了锁的机制,笔者将会在另一篇文章说明sychronized和Lock的区别。我们来看看都定义了什么接口,下面提到的获取到锁的对象就是一个Lock实例,用同一个lock实例去获取锁,才能保证是同一把锁。

1.lock(); 获取锁,如果锁不可用,则当前线程将无法被线程调度,并且休眠直到获取到锁。

2.lockInterruptibly(); 可中断获取锁,当获取锁时,如果锁可用则直接返回;如果锁不可用,当前线程将无法被线程调度,并且休眠直到线程获取到锁或者线程被其他线程中断。

3.tryLock();尝试获取锁,如果获取到了则返回true,否则返回false,利用这个方法,用户可以在获取不到锁的时候进行其他操作,该方法获取锁时不会进行等待。

4.tryLock(long time, TimeUnit unit);同上,但增加了超时设置,在获取锁时,对尝试获取一定时间。

5.unlock();释放锁。每一次获取锁以后都必须要进行释放,建议放在finally块中执行。

6.newCondition();创建一个绑定在当前锁的Condition实例。

ReentrantLock

ReentrantLock是Lock的默认实现类,ReentrantLock内部定义了一个抽象类Sync,该抽象类继承了同步器AbstractQueuedSynchronizer,Sync内部实现了tryRelease(int releases)释放锁,同时通过两个子类非公平锁NonfairSync和公平锁FairSync实现lock和tryAcquire方法。ReentrantLock内部维护了一个私有锁Sync sync,通过构造方法ReentrantLock(boolean fair)来实例化这个私有变量为公平锁或者非公平锁,ReentrantLock的一系列api都依赖于这个私有锁来实现,先来看一下Sync。

Sync

Sync继承了AbstractQueuedSynchronizer,并重写了获取锁释放锁等方法,是一个基于AbstractQueuedSynchronizer的同步器。我们来看看API:

方法描述
isLocked()锁的状态,如果当前状态不等于0,则表明锁被持有。
getHoldCount()获取持有该锁的线程数,如果该锁为排他锁,则他的状态即为持有数。
getOwner()获取该锁的持有线程。
newCondition()绑定一个ConditionObject
isHeldExclusively()判断该锁是否是排他锁
tryRelease(int var1)释放一次同步器,当前线程所持有的同步器必须是排他的,否则将抛出异常
nonfairTryAcquire(int var1)尝试获取非公平锁

重点说一下nonfairTryAcquire(int var1)和tryRelease(int var1)。

1.nonfairTryAcquire(int var1)

final boolean nonfairTryAcquire(int var1) {
            //获取当前线程
            Thread var2 = Thread.currentThread();
           //获取当前锁的状态
            int var3 = this.getState();
           //如果锁的状态为0,则表明锁是完全释放的状态,可以被获取
            if (var3 == 0) {
                //将状态改为var1
                if (this.compareAndSetState(0, var1)) {
                 //设置排他线程持有
                    this.setExclusiveOwnerThread(var2);
                 //返回true
                    return true;
                }
              //如果当前锁的状态不为0,则判断当前线程是否持有该锁,如果持有,该锁是可重入的,增//加锁的持有状态
            } else if (var2 == this.getExclusiveOwnerThread()) {
                int var4 = var3 + var1;
                if (var4 < 0) {
                    throw new Error("Maximum lock count exceeded");
                }

                this.setState(var4);
                return true;
            }

            return false;
        }

2. tryRelease(int var1)

protected final boolean tryRelease(int var1) {
           //将所状态减var1,存入局部变量var2
            int var2 = this.getState() - var1;
                 //如果当前线程并不是该锁的持有者,则抛出异常
            if (Thread.currentThread() != this.getExclusiveOwnerThread()) {
                throw new IllegalMonitorStateException();
            } else {
                
                boolean var3 = false;
             //否则判断该锁是否完全释放,如果完全释放,则清空排他线程持有
                if (var2 == 0) {
                    var3 = true;
                    this.setExclusiveOwnerThread((Thread)null);
                }
                //重置锁状态
                this.setState(var2);
                return var3;
            }
        }

Sync基本实现了一个锁该有的功能,但是如果想同时支持共享锁和排他锁,它所提供的方法显然是不足的,接下来我们看看非公平锁NonfairSync和公平锁FairSync。

NonfairSync

NonfairSync继承了Sync,在Sync的基础上lock()和tryAcquire(int var1)。tryAcquire(int var1)直接调用Sync提供的nonfairTryAcquire(var1)非公平锁获取锁,lock方法则调用了AbstractQueuedSynchronizer的acquire(int arg),对不AbstractQueuedSynchronizer了解的同学可以直接点击AbstractQueuedSynchronizer进入查看。

lock()

  final void lock() {
            //如果当前锁的状态为0,且可以通过cas改为1,则将当前锁的持有者改为当前线程,否则调用//acquire(1),尝试从队列中获取锁
            if (this.compareAndSetState(0, 1)) {
                this.setExclusiveOwnerThread(Thread.currentThread());
            } else {
                this.acquire(1);
            }

        }

 FairSync

FairSync也继承了Sync,并在Sync的基础上lock()和tryAcquire(int var1)。来看源码:

1.tryAcquire(int var1)

protected final boolean tryAcquire(int var1) {
            //获取当前线程
            Thread var2 = Thread.currentThread();
           //获取当前锁状态
            int var3 = this.getState();
           //如果当前锁完全释放
            if (var3 == 0) {
                 //则判断该同步器的队列中,该线程前是否还有节点在排队
                  //如果没有,则将同步器状态改为var1,将改锁提供给当前线程,并返回true
                if (!this.hasQueuedPredecessors() && this.compareAndSetState(0, var1)) {
                    this.setExclusiveOwnerThread(var2);
                    return true;
                }
             //如果当前所没有完全释放,同时当前线程已持有该锁,则更新锁的状态,并提供锁,否则返   //回false
            } else if (var2 == this.getExclusiveOwnerThread()) {
                int var4 = var3 + var1;
                if (var4 < 0) {
                    throw new Error("Maximum lock count exceeded");
                }

                this.setState(var4);
                return true;
            }

            return false;
        }
    }

 2.lock()

  final void lock() {
             //直接调用acquire(1),进入队列
            this.acquire(1);
        }

相信看完源码各位大神已经懂得差不多了,如果还觉得有的绕,笔者来提供两个锁的获取流程图对比一下便清楚了。

从流程图中很容易对比两种锁的获取锁机制有哪些不同,公平锁在每次获取时都会保证排在前面的线程优先获取到锁。看完Sync的构成,再来看ReentrantLock的API就很比较容易了。

方法描述
lock()获取锁,依赖于NonfairSync和FairSync
lockInterruptibly()可打断式的获取锁,依赖于AbstractQueuedSynchronizer
tryLock()尝试以非公平锁获取一次锁,获取到则返回true,获取不到则返回false,不进入队列。依赖于Sync
tryLock(long var1, TimeUnit var3)尝试在一定时间内获取锁,依赖于NonfairSync和FairSync
unlock()释放一次锁,依赖于Sync
newCondition()绑定一个ConditionObject,依赖于AbstractQueuedSynchronizer
getHoldCount()获取持有该锁的线程数,依赖于Sync
isHeldByCurrentThread()该锁是否被当前线程持有,依赖于Sync
isLocked()判断锁是否处于持有状态,依赖于Sync

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值