可重入锁机制和源码解析

2018-12-22  本文已影响0人  superxcp

一.概述:

锁分为可重入锁和不可重入锁,这两者的区别是什么?
可重入锁就是当一个线程获得了某个对象的实例并进入一个方法A,这个线程在没有释放锁的情况下能否再次进入方法A,就是可重入锁和不可重入锁的区别。

注:在看我这篇文章之前,建议先阅读以下几篇文章,对重入锁有一个大概的了解,顺序也按照我给出的顺序来阅读,我这篇文章只是对可重入锁的源码、CAS、AQS等进行一个总结,对于没有看过AQS或可重入锁的朋友来说不太容易懂。踏下心来看完我推荐的文章,基本对可重入锁(甚至互斥锁及其他锁)有一个比较深入的了解。

1.先学习一下可重入锁的使用方法
https://blog.csdn.net/u012545728/article/details/80843595
https://blog.csdn.net/soonfly/article/details/70918802

2.简单介绍可重入锁的原理,就像作者起的题目一样:轻松学习java可重入锁,这篇文章把可重入锁的机制解释的非常易懂生动。
https://blog.csdn.net/yanyan19880509/article/details/52345422

3.解释完了,直接看源码吧,这篇文章是我看过比较好的一篇介绍可重入锁源码的文章:
https://www.jianshu.com/p/7e1a1903d467

4.看完第三篇,是不是想了解下AQS:强烈推荐下面这篇讲解AQS的文章,妈呀,当我看完这篇文章之后,那叫一个通透,终于知道什么叫大牛,就是不单技术好,博客写的也NB
AQS:https://www.cnblogs.com/waterystone/p/4920797.html

5.读完AQS,是不是也想扫盲下CAS,那来吧:这篇把CAS连带Unsafe类将的比较通俗易懂
CAS:https://blog.csdn.net/mmoren/article/details/79185862

6.最后,记不记得第三篇文章提到了CAS自旋volatile变量,如果还感兴趣的朋友可以参考下这篇文章:
https://blog.csdn.net/holmofy/article/details/73824757

二.可重入锁机制及源码总结:

可重入锁分公平锁和非公平锁:线程老老实实在同步队列排队机制的锁叫公平锁,在之前线程释放锁期间可以加塞的锁叫非公平锁,可重入锁的默认锁是非公平锁。读完上面几篇文章后,可结合我的总结的源码图进行巩固(图片尺寸比较大,下载看更清楚,我传的是高清图),公平锁和非公平锁机制类似,因此这里只拿不公平锁举例。

可重入锁里面有三个内部类,Sync同步类,非公平锁类,公平锁类,其中Sync类给公平锁和非公平锁实现了一些共有方法,比如tryRelease()等方法,也给非公平锁实现了一些特定的比如nonfairTryAcquire()方法(还真是非公平啊...)。

可重入锁最基本最重要的方法就是lock()加锁和unLock()释放锁方法,这里重点介绍这两个方法。

可重入锁源码图.jpg

三.自定义同步器AQS:

谈到并发,不得不谈Reentrantlock,而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer,就像名字描述的,抽象队列同步器定义了一套多线程访问共享资源的同步器框架,许多类的实现都依赖它,比如
ReentrantLock、Semaphore、CountDownLatch等。

AQS维护了一个volatile state(代表共享资源)和FIFO线程等待队列(多线程竞争资源队列),不同自定义同步器竞争共享资源的方式不同,但自定义同步器在实现时只需实现共享资源state的获取与释放方式即可,至于线程等待队列的维护,AQS已经实现好了,主要有下面几个方法:

AQS定义两种资源共享方式:独占模式(EXCLUSIVE,只有一个线程能执行,如Reentrantlock)和共享模式(Semaphore、CountDownLatch)。一般来说,自定义同步器要么是独占的,要么是共享的,也可兼有两种模式,比如ReentrantReadWritelock。我们实现某种模式只需要继承AQS,实现tryAcquire和tryRelease方法即可,这也是AQS不是接口而是类的原因,用什么模式只要实现那个模式对应的方法。

四.CAS:

CAS全称CompareAndSwap,核心算法是这样的:

CAS(V,E,N)//V-要更新的变量;E-旧值,或者叫期望值;N-要替换的新值;

在替换为新值之前,比较下变量V是否是当前的期望值,如果相等,替换,不相等,不做动作或重复检测。

锁分为悲观锁和乐观锁,在多线程环境下,

而CAS就是一种乐观锁,他是一种系统级语言,因为系统原语的字节码指令执行时连续的,所以说CAS操作时一条CPU的原子指令操作,不会造成数据不一致的问题。

Unsafe类
我们接着深入一下,CAS在Java的实现环境中必须依赖鲜为人知的Unsafe类,可以理解为CAS操作是Unsafe类的一些方法。Unsafe这个类的方法都是被native()方法修饰的,意思是它的方法可以向操作C指针一样直接操作物理内存,直接调用操作系统底层资源执行相应任务。Unsafe类里CAS的相关操作涉及的方法如下:

//o为指定对象,offset为对象内存的偏移量,偏移量迅速定位字段并设置或获取该字段的值,
expected表示期望值,x表示要设置的值,下面3个方法都通过CAS原子指令执行操作。
public final native boolean compareAndSwapObject(Object o, long offset,Object expected, Object x);
public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);
public final native boolean compareAndSwapLong(Object o, long offset,long expected,long x);
AtomicBoolean:原子更新布尔类型
AtomicInteger:原子更新整型
AtomicLong:原子更新长整型

比如AtomicInteger的原子自增方法getAndAddInt()就是实现了CAS操作,从而保证了线程安全,有兴趣的朋友可以去看下源码。

后记:由于能力有限,若有错误或者不当之处,还请大家批评指正,一起学习交流!

上一篇 下一篇

猜你喜欢

热点阅读