不同的锁分类

  • 互斥锁:确保同一时刻只有一个线程可以执行锁定的代码。
  • 读/写锁:允许多个线程同时读取,但写入时需要独占访问。
  • 乐观锁:允许一个线程假设没有其他线程修改数据,进行读操作,而不持有完整的锁。
  • 公平锁与非公平锁:决定获取锁的顺序是随机的还是按照请求的顺序。
  • 可重入锁:同一个线程可以多次获取已经持有的锁。

synchronized锁的优化

synchronized锁自JDK 1.0以来已经经过多次优化,尤其是在JDK 1.6中引入了锁的升级优化机制,包括偏向锁、轻量级锁和重量级锁的概念,这些优化旨在减少锁操作的开销和提高多线程性能。
  • 演进过程:偏向锁 -> 轻量级锁(自旋锁)-> 重量级锁

偏向锁(Biased Locking)

  • 偏向锁是一种锁优化,它假设锁将被同一个线程多次获取。
  • 实现原理
    • 偏向锁会在对象头的Mark Word中存储获取它的线程ID,进入同步块时只需要检查这个线程ID是否为当前线程的ID,如果是,就没有竞争。

轻量级锁(Lightweight Locking)

  • 如果偏向锁失败,JVM将锁升级为轻量级锁。
  • 实现原理:
    • 在栈中创建锁记录(Lock Record)和在对象头中复制Mark Word来实现。
    • 当线程试图获取锁时,它使用CAS(Compare-And-Swap)操作尝试将锁对象的标记指向锁记录。(自旋尝试)
      • 如果成功,该线程持有锁;
      • 如果失败,表示有其他线程竞争,锁升级为重量级锁。

重量级锁(Heavyweight Locking)

  • 当有多个线程同时竞争同一个锁时,轻量级锁会升级为重量级锁。
  • 实现原理
    • 通过操作系统内的互斥量来实现,涉及到线程从用户态切换到核心态,增加了额外的开销。
    • 重量级锁能够让其他线程进入阻塞状态,直到锁被释放。

Lock

除了传统的synchronized关键字,JUC还提供了Lock,以提供更灵活、更复杂的锁操作,以适应不同的使用场景。

不同Lock的关系

  • 注:由mermaid描述,网页若无法显示,可复制后到mermaid网站在线预览

横向对比

特征/锁类型
ReentrantLock
ReentrantReadWriteLock
StampedLock
作用
提供灵活的锁管理和多条件变量支持。
分离读写操作,允许多读单写。
提供能力基的锁管理,性能优于ReadWriteLock。
使用场景
需要灵活的锁管理,多条件变量,或公平锁。
适用于读多写少的数据结构,如缓存。
读操作远超写操作的内存数据结构。
特性
可重入;支持公平和非公平锁;提供尝试锁、定时尝试锁和可中断的锁。
读锁共享,写锁排他;支持公平和非公平策略。
提供写锁、悲观读锁和乐观读;不是可重入的。
注意事项
确保锁得到释放;公平锁可能影响性能;注意死锁风险。
锁获取和释放必须匹配;不支持锁降级。
不支持条件变量;必须验证戳的有效性;确保锁的释放。

不同Lock的使用

ReentrantLock

  • 使用ReentrantLock来同步一些修改共享资源的操作。

ReentrantReadWriteLock

  • 使用ReentrantReadWriteLock来分别控制读取和写入操作,适用于读多写少的情况。

StampedLock

  • 使用StampedLock进行乐观读取,同时支持写入。乐观读取可以提高并发性,因为不阻塞写入操作。
 
Java多线程——线程同步Java多线程——线程安全
Loading...