本篇主要内容以下:java

帮你总结好的锁:面试
序号 | 锁名称 | 应用 |
---|---|---|
1 | 乐观锁 | CAS |
2 | 悲观锁 | synchronized、vector、hashtable |
3 | 自旋锁 | CAS |
4 | 可重入锁 | synchronized、Reentrantlock、Lock |
5 | 读写锁 | ReentrantReadWriteLock,CopyOnWriteArrayList、CopyOnWriteArraySet |
6 | 公平锁 | Reentrantlock(true) |
7 | 非公平锁 | synchronized、reentrantlock(false) |
8 | 共享锁 | ReentrantReadWriteLock中读锁 |
9 | 独占锁 | synchronized、vector、hashtable、ReentrantReadWriteLock中写锁 |
10 | 重量级锁 | synchronized |
11 | 轻量级锁 | 锁优化技术 |
12 | 偏向锁 | 锁优化技术 |
13 | 分段锁 | concurrentHashMap |
14 | 互斥锁 | synchronized |
15 | 同步锁 | synchronized |
16 | 死锁 | 相互请求对方的资源 |
17 | 锁粗化 | 锁优化技术 |
18 | 锁消除 | 锁优化技术 |
一、乐观锁

乐观锁编程
乐观锁
是一种乐观思想,假定当前环境是读多写少,遇到并发写的几率比较低,读数据时认为别的线程不会正在进行修改(因此没有上锁)。写数据时,判断当前 与指望值是否相同,若是相同则进行更新(更新期间加锁,保证是原子性的)。小程序
Java中的乐观锁
: CAS
,比较并替换,比较当前值(主内存中的值),与预期值(当前线程中的值,主内存中值的一份拷贝)是否同样,同样则更新,不然继续进行CAS操做。数组
如上图所示,能够同时进行读操做,读的时候其余线程不能进行写操做。安全
二、悲观锁

悲观锁服务器
悲观锁
是一种悲观思想,即认为写多读少,遇到并发写的可能性高,每次去拿数据的时候都认为其余线程会修改,因此每次读写数据都会认为其余线程会修改,因此每次读写数据时都会上锁。其余线程想要读写这个数据时,会被这个线程block,直到这个线程释放锁而后其余线程获取到锁。微信
Java中的悲观锁
: synchronized
修饰的方法和方法块、ReentrantLock
。多线程
如上图所示,只能有一个线程进行读操做或者写操做,其余线程的读写操做均不能进行。架构
三、自旋锁

mark
自旋锁
是一种技术: 为了让线程等待,咱们只须让线程执行一个忙循环(自旋)。
如今绝大多数的我的电脑和服务器都是多路(核)处理器系统,若是物理机器有一个以上的处理器或者处理器核心,能让两个或以上的线程同时并行执行,就可让后面请求锁的那个线程“稍等一会”,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁。
自旋锁
的优势: 避免了线程切换的开销。挂起线程和恢复线程的操做都须要转入内核态中完成,这些操做给Java虚拟机的并发性能带来了很大的压力。
自旋锁
的缺点: 占用处理器的时间,若是占用的时间很长,会白白消耗处理器资源,而不会作任何有价值的工做,带来性能的浪费。所以自旋等待的时间必须有必定的限度,若是自旋超过了限定的次数仍然没有成功得到锁,就应当使用传统的方式去挂起线程。
自旋
次数默认值:10次,可使用参数-XX:PreBlockSpin来自行更改。
自适应自旋
: 自适应意味着自旋的时间再也不是固定的,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定的。有了自适应自旋,随着程序运行时间的增加及性能监控信息的不断完善,虚拟机对程序锁的状态预测就会愈来愈精准。
Java中的自旋锁
: CAS操做中的比较操做失败后的自旋等待。
四、可重入锁(递归锁)

可重入锁
可重入锁
是一种技术: 任意线程在获取到锁以后可以再次获取该锁而不会被锁所阻塞。
可重入锁
的原理: 经过组合自定义同步器来实现锁的获取与释放。
-
再次获取锁:识别获取锁的线程是否为当前占据锁的线程,若是是,则再次成功获取。获取锁后,进行计数自增,
-
释放锁:释放锁时,进行计数自减。
Java中的可重入锁
: ReentrantLock、synchronized修饰的方法或代码段。
可重入锁
的做用: 避免死锁。
面试题1: 可重入锁若是加了两把,可是只释放了一把会出现什么问题?
答:程序卡死,线程不能出来,也就是说咱们申请了几把锁,就须要释放几把锁。
面试题2: 若是只加了一把锁,释放两次会出现什么问题?
答:会报错,java.lang.IllegalMonitorStateException。
五、读写锁
读写锁
是一种技术: 经过ReentrantReadWriteLock
类来实现。为了提升性能, Java 提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,若是没有写锁的状况下,读是无阻塞的,在必定程度上提升了程序的执行效率。读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由 jvm 本身控制的。
读锁: 容许多个线程获取读锁,同时访问同一个资源。

读锁
写锁: 只容许一个线程获取写锁,不容许同时访问同一个资源。

写锁
如何使用:
/** * 建立一个读写锁 * 它是一个读写融为一体的锁,在使用的时候,须要转换 */ private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
获取读锁和释放读锁
// 获取读锁 rwLock.readLock().lock(); // 释放读锁 rwLock.readLock().unlock();
获取写锁和释放写锁
// 建立一个写锁 rwLock.writeLock().lock(); // 写锁 释放 rwLock.writeLock().unlock();
Java中的读写锁:ReentrantReadWriteLock
六、公平锁

公平锁
公平锁
是一种思想: 多个线程按照申请锁的顺序来获取锁。在并发环境中,每一个线程会先查看此锁维护的等待队列,若是当前等待队列为空,则占有锁,若是等待队列不为空,则加入到等待队列的末尾,按照FIFO的原则从队列中拿到线程,而后占有锁。
七、非公平锁

非公平锁
非公平锁
是一种思想: 线程尝试获取锁,若是获取不到,则再采用公平锁的方式。多个线程获取锁的顺序,不是按照先到先得的顺序,有可能后申请锁的线程比先申请的线程优先获取锁。
优势: 非公平锁的性能高于公平锁。
缺点: 有可能形成线程饥饿(某个线程很长一段时间获取不到锁)
Java中的非公平锁:synchronized是非公平锁,ReentrantLock经过构造函数指定该锁是公平的仍是非公平的,默认是非公平的。
八、共享锁

共享锁
共享锁
是一种思想: 能够有多个线程获取读锁,以共享的方式持有锁。和乐观锁、读写锁同义。
Java中用到的共享锁: ReentrantReadWriteLock
。
九、独占锁

独占锁
独占锁
是一种思想: 只能有一个线程获取锁,以独占的方式持有锁。和悲观锁、互斥锁同义。
Java中用到的独占锁: synchronized,ReentrantLock
十、重量级锁

重量级锁
重量级锁是一种称谓: synchronized
是经过对象内部的一个叫作监视器锁(monitor
)来实现的,监视器锁自己依赖底层的操做系统的 Mutex Lock
来实现。操做系统实现线程的切换须要从用户态切换到核心态,成本很是高。这种依赖于操做系统 Mutex Lock
来实现的锁称为重量级锁。为了优化synchonized
,引入了轻量级锁
,偏向锁
。
Java中的重量级锁: synchronized
十一、轻量级锁

轻量级锁
轻量级锁
是JDK6时加入的一种锁优化机制: 轻量级锁是在无竞争的状况下使用CAS操做去消除同步使用的互斥量。轻量级是相对于使用操做系统互斥量来实现的重量级锁而言的。轻量级锁在没有多线程竞争的前提下,减小传统的重量级锁使用操做系统互斥量产生的性能消耗。若是出现两条以上的线程争用同一个锁的状况,那轻量级锁将不会有效,必须膨胀为重量级锁。
优势: 若是没有竞争,经过CAS操做成功避免了使用互斥量的开销。
缺点: 若是存在竞争,除了互斥量自己的开销外,还额外产生了CAS操做的开销,所以在有竞争的状况下,轻量级锁比传统的重量级锁更慢。
十二、偏向锁

偏向锁
偏向锁
是JDK6时加入的一种锁优化机制: 在无竞争的状况下把整个同步都消除掉,连CAS操做都不去作了。偏是指偏爱,它的意思是这个锁会偏向于第一个得到它的线程,若是在接下来的执行过程当中,该锁一直没有被其余的线程获取,则持有偏向锁的线程将永远不须要再进行同步。持有偏向锁的线程之后每次进入这个锁相关的同步块时,虚拟机均可以再也不进行任何同步操做(例如加锁、解锁及对Mark Word的更新操做等)。
优势: 把整个同步都消除掉,连CAS操做都不去作了,优于轻量级锁。
缺点: 若是程序中大多数的锁都老是被多个不一样的线程访问,那偏向锁就是多余的。
1三、分段锁

分段锁
分段锁
是一种机制: 最好的例子来讲明分段锁是ConcurrentHashMap。ConcurrentHashMap原理:它内部细分了若干个小的 HashMap,称之为段(Segment)。默认状况下一个 ConcurrentHashMap 被进一步细分为 16 个段,既就是锁的并发度。若是须要在 ConcurrentHashMap 添加一项key-value,并非将整个 HashMap 加锁,而是首先根据 hashcode 获得该key-value应该存放在哪一个段中,而后对该段加锁,并完成 put 操做。在多线程环境中,若是多个线程同时进行put操做,只要被加入的key-value不存放在同一个段中,则线程间能够作到真正的并行。
线程安全:ConcurrentHashMap 是一个 Segment 数组, Segment 经过继承ReentrantLock 来进行加锁,因此每次须要加锁的操做锁住的是一个 segment,这样只要保证每一个 Segment 是线程安全的,也就实现了全局的线程安全
1四、互斥锁

互斥锁
互斥锁与悲观锁、独占锁同义,表示某个资源只能被一个线程访问,其余线程不能访问。
-
读-读互斥
-
读-写互斥
-
写-读互斥
-
写-写互斥
Java中的同步锁: synchronized
1五、同步锁

同步锁
同步锁与互斥锁同义,表示并发执行的多个线程,在同一时间内只容许一个线程访问共享数据。
Java中的同步锁: synchronized
1六、死锁

死锁
死锁是一种现象:如线程A持有资源x,线程B持有资源y,线程A等待线程B释放资源y,线程B等待线程A释放资源x,两个线程都不释放本身持有的资源,则两个线程都获取不到对方的资源,就会形成死锁。
Java中的死锁不能自行打破,因此线程死锁后,线程不能进行响应。因此必定要注意程序的并发场景,避免形成死锁。
1七、锁粗化

锁粗化
锁粗化
是一种优化技术: 若是一系列的连续操做都对同一个对象反复加锁和解锁,甚至加锁操做都是出如今循环体体之中,就算真的没有线程竞争,频繁地进行互斥同步操做将会致使没必要要的性能损耗,因此就采起了一种方案:把加锁的范围扩展(粗化)到整个操做序列的外部,这样加锁解锁的频率就会大大下降,从而减小了性能损耗。
1八、锁消除

锁消除
锁消除
是一种优化技术: 就是把锁干掉。当Java虚拟机运行时发现有些共享数据不会被线程竞争时就能够进行锁消除。
那如何判断共享数据不会被线程竞争?
利用逃逸分析技术
:分析对象的做用域,若是对象在A方法中定义后,被做为参数传递到B方法中,则称为方法逃逸;若是被其余线程访问,则称为线程逃逸。
在堆上的某个数据不会逃逸出去被其余线程访问到,就能够把它看成栈上数据对待,认为它是线程私有的,同步加锁就不须要了。
1九、synchronized

synchronized
synchronized
是Java中的关键字:用来修饰方法、对象实例。属于独占锁、悲观锁、可重入锁、非公平锁。
-
1.做用于实例方法时,锁住的是对象的实例(this);
-
2.看成用于静态方法时,锁住的是 Class类,至关于类的一个全局锁, 会锁全部调用该方法的线程;
-
3.synchronized 做用于一个非 NULL的对象实例时,锁住的是全部以该对象为锁的代码块。它有多个队列,当多个线程一块儿访问某个对象监视器的时候,对象监视器会将这些线程存储在不一样的容器中。
每一个对象都有个 monitor 对象, 加锁就是在竞争 monitor 对象,代码块加锁是在代码块先后分别加上 monitorenter 和 monitorexit 指令来实现的,方法加锁是经过一个标记位来判断的。
20、Lock和synchronized的区别

自动挡和手动挡的区别
Lock
: 是Java中的接口,可重入锁、悲观锁、独占锁、互斥锁、同步锁。
-
1.Lock须要手动获取锁和释放锁。就比如自动挡和手动挡的区别
-
2.Lock 是一个接口,而 synchronized 是 Java 中的关键字, synchronized 是内置的语言实现。
-
3.synchronized 在发生异常时,会自动释放线程占有的锁,所以不会致使死锁现象发生;而 Lock 在发生异常时,若是没有主动经过 unLock()去释放锁,则极可能形成死锁现象,所以使用 Lock 时须要在 finally 块中释放锁。
-
4.Lock 可让等待锁的线程响应中断,而 synchronized 却不行,使用 synchronized 时,等待的线程会一直等待下去,不可以响应中断。
-
5.经过 Lock 能够知道有没有成功获取锁,而 synchronized 却没法办到。
-
6.Lock 能够经过实现读写锁提升多个线程进行读操做的效率。
synchronized的优点:
-
足够清晰简单,只须要基础的同步功能时,用synchronized。
-
Lock应该确保在finally块中释放锁。若是使用synchronized,JVM确保即便出现异常,锁也能被自动释放。
-
使用Lock时,Java虚拟机很可贵知哪些锁对象是由特定线程锁持有的。
2一、ReentrantLock 和synchronized的区别

Lock、ReentrantLock、shnchronzied
ReentrantLock
是Java中的类 : 继承了Lock类,可重入锁、悲观锁、独占锁、互斥锁、同步锁。
划重点
相同点:
-
1.主要解决共享变量如何安全访问的问题
-
2.都是可重入锁,也叫作递归锁,同一线程能够屡次得到同一个锁,
-
3.保证了线程安全的两大特性:可见性、原子性。
不一样点:
-
1.ReentrantLock 就像手动汽车,须要显示的调用lock和unlock方法, synchronized 隐式得到释放锁。
-
2.ReentrantLock 可响应中断, synchronized 是不能够响应中断的,ReentrantLock 为处理锁的不可用性提供了更高的灵活性
-
3.ReentrantLock 是 API 级别的, synchronized 是 JVM 级别的
-
4.ReentrantLock 能够实现公平锁、非公平锁,默认非公平锁,synchronized 是非公平锁,且不可更改。
-
5.ReentrantLock 经过 Condition 能够绑定多个条件
彩蛋: 讲了那么多锁,都跟阻塞相关,宝宝想听阻塞呀!
我是悟空,一只努力变强的码农!我要变身超级赛亚人啦!
你好,我是
悟空哥
,7年项目开发经验,全栈工程师,开发组长,超喜欢图解编程
底层原理。正在编写两本PDF,分别是 一、Spring Cloud实战项目(佳必过),二、Java并发必知必会。我还手写了2个小程序
,Java刷题小程序,PMP刷题小程序,点击个人公众号菜单打开!另外有111本架构师资料以及1000道Java面试题,都整理成了PDF,能够关注公众号 悟空聊架构 回复悟空
领取优质资料。
转发->在看->点赞->收藏->评论!!!是对我最大的支持!
- END -
本文分享自微信公众号 - 悟空聊架构(PassJava666)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。