开发基础
JAVA
JUC
多线程
Lock锁
并发容器类
JUC辅助类
JUC底层知识
JVM
本文档使用 MrDoc 发布
-
+
首页
Lock锁
## synchronized synchronized 是 Java 中用于控制多线程并发访问的关键字,它可以保证一个线程在执行特定代码段时,其他线程不能执行该代码段,从而避免多线程环境下的数据不一致问题。 ### synchronized 的关键信息 1. 锁的四种状态 synchronized 的锁有四种状态,分别是: 无锁状态:对象未被任何线程锁定。 偏向锁:锁偏向于某个线程,该线程可以快速获取锁。 轻量级锁:通过 CAS 操作尝试获取锁,适用于低竞争场景。 重量级锁:使用操作系统提供的互斥锁机制,适用于高竞争场景。 2. 锁的性能优化 锁消除:JVM 在某些情况下会自动消除锁,以提高性能。 锁粗化:将多个连续的锁操作合并为一个锁操作,减少锁的开销。 3. synchronized 与 Lock 的区别 性能:在高并发场景下,Lock 的性能通常优于 synchronized,因为 Lock 支持更细粒度的锁控制和非阻塞机制。 灵活性:Lock 提供了更灵活的锁控制,如尝试获取锁、公平锁等。 解锁方式:synchronized 无需手动释放锁,而 Lock 需要手动释放锁。 4. synchronized 的使用 修饰方法:锁住当前对象实例或类对象。 修饰代码块:锁住指定的对象。 ### 锁升级 synchronized的锁升级机制是指在不同的线程竞争情况下,锁的状态会从低级别逐渐升级到高级别,以提高性能和资源利用率。锁升级的过程如下: **无锁状态:** 当对象刚创建时,处于无锁状态,任何线程都可以自由访问和修改对象的数据。 **偏向锁:** 当一个线程第一次访问对象时,锁会从无锁状态升级为偏向锁。偏向锁会将对象头中的 Mark Word 设置为当前线程的 ID,后续同一线程再次访问时,只需检查 Mark Word 是否是自己的线程 ID 即可,无需额外的 CAS 操作。 **轻量级锁:** 当有其他线程尝试获取已经被偏向的锁时,偏向锁会升级为轻量级锁。轻量级锁通过 CAS 操作尝试获取锁,如果成功则继续执行,否则会进一步升级为重量级锁。 **重量级锁:** 当轻量级锁的 CAS 操作失败,表示存在线程竞争,锁会升级为重量级锁。重量级锁使用操作系统提供的互斥锁机制,线程在竞争锁时会进入阻塞状态。 ### 锁降级 锁降级是指在某些情况下,锁的状态从高级别降为低级别。锁降级的主要目的是为了提高性能和资源利用率。例如,在添加了写锁后再添加读锁,可以保证数据的可见性 ## Lock锁 相比同步锁,JUC包中的Lock锁的功能更加强大,它提供了各种各样的锁(公平锁,非公平锁,共享锁,独占锁……),所以使用起来很灵活。 Lock是一个接口,这里主要有三个实现:ReentrantLock、ReentrantReadWriteLock.ReadLock、ReentrantReadWriteLock.WriteLock ## ReentrantLock可重入锁 * ReentrantLock:悲观的独占的排他的互斥的可公平可不公平的可重入锁 ``` ReentrantLock lock = new ReentrantLock(true/false); lock.lock()/unlock(); ``` * 可重入性: 可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁。Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。 * 公平锁 ReentrantLock还可以实现公平锁。所谓公平锁,也就是在锁上等待时间最长的线程将获得锁的使用权。通俗的理解就是谁排队时间最长谁先执行获取锁。 * 限时等待 这个是什么意思呢?也就是通过我们的tryLock方法来实现,可以选择传入时间参数,表示等待指定的时间,无参则表示立即返回锁申请的结果:true表示获取锁成功,false表示获取锁失败。我们可以将这种方法用来解决死锁问题。 ### ReentrantLock和synchronized区别详解 (1)synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。 (2)synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。 (3)synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以响应中断。 ## ReentrantReadWriteLock读写锁 在并发场景中用于解决线程安全的问题,我们几乎会高频率的使用到独占式锁,通常使用java提供的关键字synchronized或者concurrents包中实现了Lock接口的ReentrantLock。它们都是独占式获取锁,也就是在同一时刻只有一个线程能够获取锁。而在一些业务场景中,大部分只是读数据,写数据很少,如果仅仅是读数据的话并不会影响数据正确性(出现脏读),而如果在这种业务场景下,依然使用独占锁的话,很显然这将是出现性能瓶颈的地方。针对这种读多写少的情况,java还提供了另外一个实现Lock接口的**ReentrantReadWriteLock**(读写锁)。**读写锁允许同一时刻被多个读线程访问,但是在写线程访问时,所有的读线程和其他的写线程都会被阻塞**。 读写锁的特点: 1. 写写不可并发 2. 读写不可并发 3. 读读可以并发 ### 读写锁的锁降级 锁降级就是从写锁降级成为读锁。在当前线程拥有写锁的情况下,再次获取到读锁,随后释放写锁的过程就是锁降级。 ### 读写锁总结 1. 支持公平/非公平策略 2. 支持可重入 同一读线程在获取了读锁后还可以获取读锁 同一写线程在获取了写锁之后既可以再次获取写锁又可以获取读锁 1. 支持锁降级,不支持锁升级 2. 读写锁如果使用不当,很容易产生“饥饿”问题: 在读线程非常多,写线程很少的情况下,很容易导致写线程“饥饿”,虽然使用“公平”策略可以一定程度上缓解这个问题,但是“公平”策略是以牺牲系统吞吐量为代价的。 3. Condition条件支持 写锁可以通过`newCondition()`方法获取Condition对象。但是读锁是没法获取Condition对象,读锁调用`newCondition() `方法会直接抛出`UnsupportedOperationException`。
admin
2025年3月4日 20:07
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Rancher
Jenkins
ADMIN-UI
VBEN-ADMIN-UI
RUST-FS
MinIO
mindoc
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码