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

文章目录

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

锁分类

按照框架技术分类:

  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适用

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

相关推荐
手握风云-27 分钟前
数据结构(Java版)第一期:时间复杂度和空间复杂度
java·数据结构
坊钰30 分钟前
【Java 数据结构】时间和空间复杂度
java·开发语言·数据结构·学习·算法
飞升不如收破烂~35 分钟前
Redis的String类型和Java中的String类在底层数据结构上有一些异同点
java·数据结构·redis
苹果酱056739 分钟前
windows安装redis, 修改自启动的redis服务的密码
java·开发语言·spring boot·mysql·中间件
feilieren43 分钟前
信创改造 - TongRDS 替换 Redis
java·spring boot·后端
Allen Bright1 小时前
Jedis连接池的操作
java·redis
庞传奇1 小时前
【LC】560. 和为 K 的子数组
java·算法·leetcode
@糊糊涂涂2 小时前
MAC借助终端上传jar包到云服务器
java·服务器·macos·jar
东方巴黎~Sunsiny2 小时前
给定数字 [3, 30, 34, 5, 9] 拼接成的最大数字,使用java实现
java·开发语言
daiyang123...2 小时前
Java 复习 【知识改变命运】第九章
java·开发语言·算法