java中的Lock深度解析

1.概述

java中的加锁方式,除了有synchronized之外,还有Lock。

可重入机制

我们对比一下可重入和不可重入的概念,这样就可以更形象的理解可重入。

所谓不可重入就是说所有访问这个锁的代码块都需要先获取锁标记才可以进入,锁标记被释放之后,其他的访问才可以重新竞争锁标记然后竞争成功进入(代码块拿到了锁标记)。

所谓可重入就是说一个获得锁标记的线程可以多次访问这个锁,既这个线程拥有了访问这个锁的权限可以自由进出,这个线程内部,可以多次访问临界资源而不会被锁阻塞(其实就是这个线程拿到了所标记,线程内部都可以拿着这个锁标记进入锁,只不过每进入一次会有相应的记录)。

比如说A类中有个方法

public synchronized methodA1(){

methodA2();

}

而且

public synchronized methodA2(){

       //具体操作

}

如果锁是不可重入的,当执行methodA1时,先获取了锁,然后再执行methodA2的时候因为锁还没有释放,methodA2无法获取锁,所以就形成了死锁的局面

但是如果锁是可重入的,那么就不会出现死锁的局面了,methodA2也可以获取到所标记。

*:可重入锁可以避免某些场景下死锁的出现

 

AQS

它是一个java实现的同步工具,所有的lock都是基于这个工具实现的。其内部就是一个双向的链表和一些属性,锁通过这些属性来实现锁机制。不管是ReentrantLock

还是ReentrantReadWriteLock都是采用了AQS实现,所以我们重点来研究一下AQS的原理。

AQS的形态:

AQS通过一个全局的state和一个双向的列表来管理线程的同步

State

维护同步器的状态,也可以说是可重入次数。

State=0表示当前没有线程占用这个同步器

State>0表示已经被占用,并且state的数值表示重入的次数

双向的列表

由head和tail两个指针维护一个FIFO的队列,表示同步队列,用于存放未争抢到资源的线程,来做等待排队。

首先来介绍一下Node,它是AQS类的一个内部类,它就是双向列表中的一个节点。

Node的属性

再来看下AQS中有那些成员:

在AQS中有两个方法是没有实现的,这两个方法是由具体的实现类去实现的,ReentrantLock里面都已经实现了这两个方法。

 

 

AQS流程:

现在模拟ABC三个线程的争抢过程。

A线程首先调用lock(),这时候A是第一个来争抢锁的线程,它会将state的值改为1(state大于0证明这个锁已经被其他线程获取)并且将exclusiveOwnerThread改为当前争抢到锁的线程,这代表着争抢锁已经成功,下面就开始执行A的逻辑。

 

A正在运行的时候,B线程也调用了lock()方法。首先B线程会去尝试获得锁,由于A线程已经获得锁正在运行,B线程尝试获取的时候发现A已经获得了锁,所以B线程只能排队,等待A线程执行完再抢占锁。具体的排队过程如下:

首先B通过自旋的方式将自己封装成一个node并添加到同步队列的尾部。为什么自旋:因为为了防止并发情况,使用乐观锁的方式解决了并发

然后再将自己的node中的waitStatus状态改为-1(signal),挂起,等待唤醒。

 

再接着A线程现在执行完毕,开始释放锁,释放锁的过程是:

将state值一步步改为0,exclusiveOwnerThread记为null。将同步队列首部的第一个Node(空的那个node不算在内)从同步队列中删除,然后唤醒这个node对应的线程,让它参与锁的竞争。

 

B线程被唤醒,继续去获取锁,这时候C线程也加入了进来,也要去获取锁。这种情况就看谁改的快。

B获取成功就会将state和exclusiveOwnerThread修改为自己,C进入同步队列排队。

C获取成功也会将state和exclusiveOwnerThread修改为自己,B会继续进入队列排队。

 

 

ReentrantReadWriteLock

 

 

ReentrantLock

ReentrantLock是对AQS的一个使用。它所有的功能都是基于调用AQS的方法实现的。

在ReentrantLock内部有两个内部类:

 

这两个内部类一个是公平锁的实现,一个是非公平锁的实现,在创建ReentrantLock对象时可以指定时这个锁是公平的还是非公平的(默认非公平)。指定完公平性之后,ReentrantLock就会根据你指定的值去调用这某一个内部类的方法实现锁。

 

Lock和synchronized的区别

锁的实现:

Synchronized是依赖于JVM实现的,而ReenTrantLock是JDK实现的,有什么区别,说白了就类似于操作系统来控制实现和用户自己敲代码实现的区别。前者的实现是比较难见到的,后者有直接的源码可供阅读。

 

性能的区别:

在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,但是自从Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized,其实synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。

 

功能区别:

便利性:很明显Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。

但是Lock的实现更为灵活,我们可以根据实际的情况去选择使用,重入锁还是读写锁,公平锁还是非公平锁等等等。

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值