在互联网公司面试中,很多小伙伴都被问到过关于锁的问题 。今天,我给大家一次性把Java并发锁的全家桶彻底讲明白 。包括互斥锁、读写锁、重入锁、公平锁、悲观锁、自旋锁、偏向锁等等等等 。视频有点长,大家一定要全部看完,保证你会醍醐灌顶 。
1、锁的由来在并发编程中 , 经常会遇到两个以上的线程访问同一个共享变量,当同时对共享变量进行读写操作时,就会产生数据不一致的情况 。

文章插图
随着线程并发技术的发展,在多线程环境中,对线程访问资源的限制也越来越多 。为了保证资源获取的有序性和占用性 , 都是通过并发锁来控制的 。
2、锁的应用场景下面,我根据个人经验以及并发场景下线程的处理逻辑,总结为以下7个场景,不同场景使用不同的锁 。
1)某个线程是否锁住同步资源的情况如果要锁住同步资源则使用悲观锁,不锁住同步资源使用乐观锁 。所谓悲观锁,就是每次拿数据的时候都认为会有别人修改,所以在读数据的时候都会上锁 , 其他线程数据就会阻塞 , 直到拿到锁 。

文章插图
举个例子,假设厕所只有一个坑位,悲观锁就是上厕所会第一时间把门反锁上,这样其他人上厕所只能在门外等候,这就是阻塞 。

文章插图
而乐观锁就是开着门,当然在这个场景下一般也不会这么做 。所以,乐观锁,就是每次拿数据的时候都假设为别人不会修改,所以不会上锁;只是在更新数据的时候去判断之前有没有别的线程更新了这个数据 。如果这个数据没有被更新,当前线程将自己修改的数据成功写入 。如果数据已经被其他线程更新了,要么报错,要么自动重试 。

文章插图
乐观锁与悲观锁是一种广义上的概念,没有谁优谁劣 。乐观锁适用于写少读多的场景,因为不用上锁、释放锁,省去了锁的开销,从而提升了吞吐量 。而悲观锁适用于写多读少的场景,因为线程间竞争激励,如果使用乐观锁会导致线程不断进行重试,这样反而还降低了性能 。
2)多个线程是否共享一把锁的情况如果在并发情况下 , 多个线程共享一把锁就是使用共享锁 , 如果不能共享一把锁就是排它锁或者叫独占锁、独享锁 。共享锁是指锁可被多个线程所持有 。如果一个线程对数据加上共享锁后,那么其他线程只能对数据再加共享锁,不能加独占锁 。获得共享锁的线程只能读数据,不能修改数据 。

文章插图
在 JDK 中 ReentrantReadWriteLock 就是一种共享锁 。而独占锁是指锁一次只能被一个线程所持有 。如果一个线程对数据加上排他锁后 , 那么其他线程不能再对该数据加任何类型的锁 。获得独占锁的线程即能读数据又能修改数据 。

文章插图
JDK中的synchronized和J.U.C(java.util.concurrent)包中Lock的实现类都是独占锁 。另外 , 互斥锁是独占锁的一种常规实现 , 是指某一资源同时只允许一个访问者对其进行访问 , 具有唯一性和排它性 。

文章插图
互斥锁一次只能一个线程拥有互斥锁,其他线程只有等待 。而读写锁是共享锁的一种具体实现 。读写锁管理一组锁,一个是只读的锁,一个是写锁 。读锁可以在没有写锁的时候被多个线程同时持有,而写锁是独占的 。写锁的优先级要高于读锁,一个获得了读锁的线程必须能看到前一个释放的写锁所更新的内容 。读写锁相比于互斥锁并发程度更高,每次只有一个写线程,但是同时可以有多个线程并发读 。

文章插图
在 JDK 中定义了一个读写锁的接口ReadWriteLock,如源码所示:
public interface ReadWriteLock {/* 获取读锁 */Lock readLock();/* 获取写锁 */Lock writeLock();}
ReentrantReadWriteLock 实现了ReadWriteLock接口,ReentrantReadWriteLock 支持锁降级不支持锁升级,可以由写锁降为读锁 。3)多个线程竞争时是否要排队的情况多个线程竞争排队获取锁的情况,使用公平锁,如果 , 使用非公平锁 。所谓公平锁是指多个线程按照申请锁的顺序来获取锁 , 这里类似排队买票,先来的人先买,后来的人在队尾排着,这是公平的 。
推荐阅读
- 如何把Java代码玩出花?JVM Sandbox入门教程与原理浅谈
- 如何删除微信零钱明细(零钱明细账能一次全部删除吗)
- QQ如何手动升级(电脑上qq怎么升级)
- qq安全如何升级(qq安全中心不能直接改密码了)
- 如何更新电脑QQ版本(电脑qq关闭自动更新)
- 什么是 X.509 证书以及它是如何工作的?
- 新qq如何快速升级qq怎么快速升级(在qq怎么快速升级等级)
- 英雄联盟怎么玩好(新人如何玩好英雄联盟)
- 如何才能玩好英雄联盟(怎么样才能玩英雄联盟最好)
- Java安全之CC2