锁总结(分类、介绍、选择)

文章目录

锁:用于控制多个线程对共享资源访问的机制,确保任何给定时间只有一个线程能访问共享资源,防止多线程并发访问引起的数据竞争和不一致问题。

锁分类

按照框架技术分类:

  1. java中的锁
    • sychronized关键字
    • Lock接口
  2. mysql中的锁
    • 表锁
    • 行锁
    • 读写锁
    • 自增锁
  3. 分布式锁

按照公平性/非公平性分类:

  • 公平锁:按照线程请求锁的顺序来授予锁,确保先请求的线程优先获取锁。
  • 非公平锁:不保证请求顺序,可能导致某些线程长期得不到锁。通常性能更好,但可能引入"饥饿"现象。

按照乐观/悲观分类:

  • 乐观锁:认为自己这个线程在使用数据的时候不会有其他线程来修改数据,在更新数据前先判断有没有其他线程修改了该数据。如果没有,将更新的数据写入;如果有,就会根据不同的情况执行不同的操作(例如,报错)
  • 悲观锁:认为自己这个线程在使用数据的时候会有其他线程来修改数据,所以在获取数据之后会加锁,防止被别的线程修改。

悲观锁适合 操作 的场景(确保写操作数据正确),乐观锁适合操作多的场景(可以使读操作性能提升)。

其他:

  • ThreadLocal:Java中的一种类,虽然不属于锁的范畴,但也能解决线程安全问题。在多线程访问过程中,如果把数据放在ThreadLocal类中,不同线程得到的是该变量数据的副本,可以避免线程安全问题。

锁介绍

  1. sychronized:
    • 作用域:Java中的关键字,可作用于代码块、方法。
    • 公平性:非公平锁,不管多线程的顺序性,所有线程都可以尝试获取锁资源。
    • 由于在sychronized中锁的获取、释放是++隐式的++ ,为了方便锁的管理,避免不必要的资源损耗,在synchronized中存在一个锁升级 的过程(无锁->偏向锁->轻量级锁->重量级锁,该过程不可逆,只能升级不能降级),具体来说:
      • 无锁:就是没有锁,所有线程都可以对资源进行访问,不存在锁竞争关系。
      • 偏向锁:也就是偏向于第一个访问锁资源的线程,第一个访问锁资源的线程会得到偏向锁,锁对象中会保存线程的ID。一旦有第二个线程来访问锁资源,偏向锁将撤销线程ID信息,升级为轻量级锁。(适用于单线程)
      • 轻量级锁:线程以CAS(Compare And Switch)无锁机制来尝试获取锁,如果已有线程占有锁,线程将进入自旋状态(占有CPU资源,等待再次获取锁),jvm中会设置自旋等待的最大次数,如果有线程达到最大自旋次数却还是没能获取到锁资源,轻量级锁将会升级为重量级锁。轻量级锁一般适用于加在执行时间较短的代码上,因为自旋虽然能减少CPU上下文切换的开销,但同时也会占用CPU资源,处于忙等的状态。
      • 重量级锁:没有获得锁资源的线程将阻塞,等待再次唤醒。
  2. Lock接口:相对于synchronized,实现了更灵活的锁机制。
    • 可显式的获取/释放锁;

      java 复制代码
      .lock();//获取锁
      .unlock();//释放锁
    • 可实现更复杂的锁策略:

      • 可中断锁:tryLock(long timeout, TimeUnit unit)
      • Condition对象:与 Lock 结合使用,允许线程在特定条件下等待和唤醒,适用于复杂的线程协作。
    • 含多个锁的实现类:

      • ReentrantLock:可重入锁(同一线程可多次获取同一把锁)

        • 公平性:可选公平/不公平,构造方法中可设置公平性

          java 复制代码
          //构造函数
          public ReentrantLock(boolean fair) {
              sync = fair ? new FairSync() : new NonfairSync();
          }
          //使用
          private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
      • ReentrantReadWriteLock:读写锁

        • 公平性:可选(与ReentrantLock类似)
        • 允许多个线程同时读数据,但写操作会独占锁
      • StampedLock:另一种高效的读写锁。

        • 读操作频繁的情况下,StampedLock的性能优于ReentrantReadWriteLock。
        • StampedLock允许在乐观读和悲观读之间进行选择,提供了更大的灵活性。在某些情况下,如果乐观读失败,可以选择获取悲观读锁。而ReentrantReadWriteLock本身不实现乐观锁的机制。
  3. mysql中的锁:
    • 表锁:MyISAM存储引擎仅能实现表锁,当修改某一行数据时,整张表都会被锁起来。
    • 行锁:依赖于InnoDB存储引擎,使用 MVCC 来实现行级锁(依赖于在每一行数据中维护一个隐藏的版本号)。
    • 读写锁:MySQL 存在读写锁的概念,允许多个线程同时读取数据,但在写入时会独占锁。
    • 自增锁:在插入自增字段时,MySQL 会加自增锁以保证自增值的唯一性。
  4. 分布式锁:
    • 适用于分布式环境下,对于多个jvm来管理的锁资源,能够实现分布式锁的技术包括但不限于:redis、zookeeper等

锁选择

  1. synchronized:
    • 适用于较为简单的同步场景
    • 考虑代码简洁性(不需要显式的获取、释放锁)
    • 无需公平性
  2. Lock:
    • 较复杂的锁策略
    • 高竞争环境
    • 需要公平性
  3. 分布式锁:
    • 分布式环境下,多个jvm适用

以上为个人学习分享,如有问题,欢迎指出:)

相关推荐
Algorithm15765 分钟前
JVM是什么,与Java的关系是什么,以及JVM怎么实现的跨平台性
java·开发语言·jvm
遇见你真好。1 小时前
SpringBoot整合quartz定时任务
java·springboot·quartz
一颗甜苞谷1 小时前
开源一款基于 JAVA 的仓库管理系统,支持三方物流和厂内物流,包含 PDA 和 WEB 端的源码
java·开发语言·开源
攻心的子乐1 小时前
idea使用svn
java·ide·intellij-idea
程序员大佬超1 小时前
IDEA解决 properties 文件乱码问题
java·ide·intellij-idea
秋恬意1 小时前
LinkedList 源码分析
java·开发语言·面试
隔窗听雨眠1 小时前
深入理解Redis的四种模式
java·redis·mybatis
lexusv8ls600h1 小时前
微服务设计模式 - 重试模式(Retry Pattern)
java·spring boot·微服务
Viktor_Ye2 小时前
高效数据集成:易仓退款订单与金蝶销售退货单的自动化对接
java·运维·自动化
feilieren2 小时前
leetcode - 684. 冗余连接
java·开发语言·算法