背景java
有人对Java主流锁作了下面全面的梳理。梳理的确实挺好的。可是我看到这张图,第一个感受是:记不住。面试
由于分了太多类,彼此之间没有什么联系。作PPT能够。若是聊天或者面试,不用纸笔的状况下,就不太好描述了。也不利于对原理和应用的理解。数据库
基于上述的考虑,我就本身系统的梳理一下锁,但愿能够有助于你们理解和记忆,以致于最后在工做中获得很好的应用。并发
先说线程锁再说分布式锁。分布式
线程锁线程
概述 指针
这里说的线程锁是Java线程锁,从原理上各个语言应该都比较类似。有不少维度的划分方式,我比较建议的是从大面上分为乐观锁和悲观锁。blog
乐观锁主要是自旋+CAS的方式,好比JUC(java.util.concurrent包)的原子类。 继承
悲观锁主要用synchronized关键字的隐式锁和基于AQS的显示锁。队列
上面三段总结以下:
悲观锁的实现原理
1>synchronized关键字
随着java版本升级,synchronized关键字虽然是用C++写的,可是原理和JCU包的ReentrantLock很类似。synchronized关键字有4种锁状态:无锁、偏向锁、轻量级锁、重量级锁。无锁相似于ReentrantLock的交替执行,没有并发,就不涉及锁;偏向锁相似于ReentrantLock的可重入的概念,使得已经获取到锁的线程能够屡次获取锁;轻量级锁解决的问题是尽可能避免线程切换,使用的方法也和ReentrantLock类似,是自旋+CAS的方式;重量级锁依赖于管程monitor来实现,和ReentrantLock同样都涉及用户态和内核态切换。
根据这个咱们再来补充一下Java线程锁的思惟导图:
2>基于基于AQS的显示锁
基于AQS的显示锁我以前看过一些源码。这里面比较经典的是ReentrantLock。这是可重入锁,就是同一个线程能够反复进入加锁的线程。若是想实现不可重入锁也很简单。把可重入锁对当前线程作特殊处理的部分去掉就行了。
其余JCU下locks包里的锁好比读写锁就是将锁细化成了读锁和写锁。读锁是共享锁的实现,写锁是排他锁的实现。
ReentrantLock能够使用公平锁和非公平锁两种方式,公平锁和非公平锁各自继承了AQS。区别只是非公平锁在须要加锁时先直接尝试是否能够获取锁成功,而公平锁是先看本身是否须要排队。
下面以ReentrantLock的公平锁为例来简单聊一下AQS的源码。AQS核心是实现了CLH队列。
AQS有head、tail、持有锁的线程、状态4个主要的成员变量。
利用head!=tail就是说AQS是否未被初始化来判断是否交替执行,交替执行则不用加锁;若是须要加锁则判断是否就是当前拥有锁的线程,是的话,将进入次数+1;若是不是则判断是否须要初始化AQS,须要的话先初始化一个dummy header,再将本身加入队尾,若是是队列里dummy header的指针指向的节点,则它为先自旋判断是否能够获取锁;若是不是dummy header指针指向的节点,则使用park让出cpu。当dummy header的指针指向的节点获取到锁以后,会将head指向本身,同时将本身这个Node节点的当前线程设置为空,将本身设置为dummy header,同时将原来dummy header的指针都设置为null,使得原dummy header成为一个没有引用的节点,便于垃圾回收。
根据这个咱们再来补充一下Java线程锁的思惟导图:
分布式锁
无论是线程锁仍是分布式锁,都实现了tryLock、lock、unlock三个方法。
tryLock的语义是非阻塞锁,尝试获取锁,成功返回true,不成功返回false;主流lock语义是阻塞锁。实现通常基于tryLock来作自旋,不成功的时候也会有像ReentrantLock同样的阻塞操做。
常见的分布式锁实现以及数据库锁的实现详见以前写的文章:《MySQL常见6个考题在实际工做中的运用》这里就再也不赘述了。
总结
本篇文章在介绍知识点是次要的,主要是展现了总结思考的思路,但愿能对读者朋友们的思考问题方法上有所帮助,仅作参考。