展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > Java与MySQL锁相关的知识点有哪些?

Java与MySQL锁相关的知识点有哪些?

锁的定义在计算机程序中锁用于独占资源,获取到锁才可以操作对应的资源。锁的实现锁在计算机底层的实现,依赖于CPU提供的CAS指令(compareandswsp),对于一个内存地址,会比较原值以及尝试去修改的值,通过值是否修改成功,来表示是否强占到了这个锁。JVM中的锁jvm中,有2个常用的锁synchronizedsynchronized是java提供的关键字锁,可以锁对象,类,方法。在JDK1.6以后,对synchronized进行了优化,增加了偏向锁和轻量锁模式,现在synchronized锁的运行逻辑

本文目录

      锁的定义

      在计算机程序中锁用于独占资源,获取到锁才可以操作对应的资源。

      锁的实现

      锁在计算机底层的实现,依赖于CPU提供的CAS指令(compare and swsp),对于一个内存地址,会比较原值以及尝试去修改的值,通过值是否修改成功,来表示是否强占到了这个锁。

      JVM中的锁

      jvm中,有2个常用的锁

      synchronized

      synchronized是java提供的关键字锁,可以锁对象,类,方法。
      在JDK1.6以后,对synchronized进行了优化,增加了偏向锁和轻量锁模式,现在synchronized锁的运行逻辑如下:

      • 在初始加锁时,会增加偏向锁,即“偏向上一次获取该锁的线程”,在偏向锁下,会直接CAS获取该锁。该模式大大提高了单线程反复获取同一个锁的吞吐情况,在Java官方看来,大部分锁的争抢都发生在同个线程上。

      • 如果偏向锁CAS获取失败,说明当前线程与偏向锁偏向的线程不同,偏向锁就会升级成轻量锁,轻量锁的特点就是通过自旋CAS去获取锁。

      • 如果自旋获取失败,那么锁就会升级成重量锁,所有等待锁的线程将被JVM挂起,在锁释放后,再由JVM统一通知唤醒,再去尝试CAS锁,如果失败,继续挂起。

      很显然,偏向锁设计的目的是“在Java官方看来,对同一个锁的争抢大部分都发生在同个线程上”。
      轻量锁设计的目的是“在短期内,锁的争抢通过自旋CAS就可以获取到,短时间内的CPU自旋消耗小于线程挂起再唤醒的消耗”。
      重量锁就是最初优化前的synchronized的逻辑了。

      ReentrantLock

      说到ReentrantLock,就不得不说到JUC里的AQS了。
      AQS全称AbstractQueueSynchronizer,几乎JUC里所有的工具类,都依赖AQS实现。
      AQS在java里,是一个抽象类,但是本质上是一种思路在java中的实现而已。
      AQS的实现逻辑如下:

      • 构造一个队列

      • 队列中维护需要等待锁的线程

      • 头结点永远是持有锁(或持有资源)的节点,等待的节点在头结点之后依次连接。

      • 头结点释放锁后,会按照顺序去唤醒那些等待的节点,然后那些节点会再次去尝试获取锁。

      在synchronized锁优化以后,AQS的本质与synchronized并没有太大不同,两者的性能也并没有太大差距了,所以AQS现在的特点是:

      • 是在java api层面实现的锁,所以可以实现各种并发工具类,操作也更加灵活

      • 因为提供了超时时间等机制,操作灵活,所以不易死锁。(相同的,如果发生死锁,将更难排查,因为jstack里将不会有deadlock标识)。

      • 可以实现公平锁,而synchronized必定是非公平锁。

      • 因为是JavaApi层实现的锁,所以可以响应中断。

      到这里你会发现,其实ReentrantLock可以说是synchronized在JavaApi层的实现。

      Mysql 锁

      共享锁(S) 与排它锁(X)

      作用范围

      这两种锁都包括行级锁和表级锁。
      获取共享锁时,如果该数据被其他事务的排它锁锁住,则无法获取,需要等待排它锁释放。

      意向锁

      作用范围

      意向锁为表锁,在获取表锁之前,一定会检查意向锁。

      意图锁定协议如下:

      在事务获得表中某行的共享锁之前,它必须首先获得表上的 IS 锁或更强的锁。

      在事务获得表中行的排他锁之前,它必须首先获得表的 IX 锁。

      在获取任意表锁的共享锁或排它锁之前,一定会检查该表上的共享锁。

      表锁以及意向锁的互斥规则如下:
      X IX S IS
      X Conflict Conflict Conflict Conflict
      IX Conflict Compatible Conflict Compatible
      S Conflict Conflict Compatible Compatible
      IS Conflict Compatible Compatible Compatible

      意向锁的作用在于:在获取表锁时,可以通过意向锁来快速判断能否获取。

      因为获取行级锁时,会先获取对应的意向锁,这样另外的事务在获取表锁时就可以通过意向锁快速的判断,而不需要每行去扫描。

      特别注意的是,意向锁是可以叠加的,即会存在多个,如T1事务获取了意向锁IX1和行级锁X1,T2事务依旧可以获取意向锁IX2和行级锁X2,所以仅在获取表级锁之前,才会检查意向锁。

      记录锁

      记录锁生效在索引上,用以在SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE时保护该行数据不被其他事务更改。

      记录锁在没有索引时依旧会生效,因为innodb会为每张表创建一个隐藏的索引。

      记录锁是最基本的行锁。

      间隙锁

      间隙锁生效在索引上,用于锁定索引值后的行,防止插入,在select from table where index=? for update时会生效,例如index=1,则会锁住index=1索引节点相关的行,防止其他事务插入数据。

      但是并不会防止update语句,哪怕update的数据不存在。

      Next-Key Locks

      这个锁是记录锁和间隙锁的组合,简而言之在select from table where index=? for update时,既会有间隙锁防止insert,也会有记录锁在index上防止这一条数据的update和delete。这个Next-key只是对这两种锁的一种概括,因为这两种锁在select for update时通常会一起出现。

      Insert Intention Locks

      插入意向锁,和意向锁类似。不过是特殊的间隙锁,并不发生在select for update,而是在同时发生insert时产生,例如在两个事务同时insert索引区间为[4,7]时,同时获得该区间的意向锁,此时事务不会阻塞,例如A:insert-5,B:insert-7,此时不会阻塞两个事务。

      插入意向锁是一个特殊的间隙锁,是为了防止正常间隙锁锁区间的情况下,insert频繁阻塞而设计的,例如A:insert-5,B:insert-7,如果没有插入意向锁,那么5和7都要去尝试获取间隙锁,此时第二个事务就会被阻塞,但是通过插入意向锁,第二个事务就不会被阻塞,只有到插入的行确实冲突,才会被阻塞。

      AUTO-INC Locks

      自增锁,这个锁很明显是表级insert锁,为了保证自增主键的表的主键保持原子自增。

      对于锁这个东西,大家应该多去理解各种锁设计运行的原理和模型,这样在加深理解后,在使用起来才会更加深入和透彻。

      常见锁使用的场景和用法

      double check

      众所周知,mysql的事务对防止重复插入并没有什么卵用,唯一索引又存在很多缺点,业务上最好不要使用,所以一般来说防止重复插入的通用做法就是使用分布式锁,这就有一种比较常用的写法。

      final WeekendNoticeReadCountDO weekendNoticeReadCountDO = weekendNoticeReadRepositoryService.selectByNoticeId(noticeRequestDTO.getNoticeId());
      if (weekendNoticeReadCountDO == null) {
          final String lockKey = RedisConstant.LOCK_WEEKEND_READ_COUNT_INSERT + ":" + noticeRequestDTO.getNoticeId();
          ClusterLock lock = clusterLockFactory.getClusterLockRedis(
              RedisConstant.REDIS_KEY_PREFIX,
              lockKey
          );
          if (lock.acquire(RedisConstant.REDIS_LOCK_DEFAULT_TIMEOUT)) {
              //double check
              final WeekendNoticeReadCountDO weekendNoticeReadCountDO = weekendNoticeReadRepositoryService.selectByNoticeId(noticeRequestDTO.getNoticeId());
              if (weekendNoticeReadCountDO == null) {
                  try {
                      lock.execute(() -> {
                          WeekendNoticeReadCountDO readCountDO = new WeekendNoticeReadCountDO();
                          readCountDO.setNoticeId(noticeRequestDTO.getNoticeId());
                          readCountDO.setReadCount(1L);
                          readCountDO.setCreateTime(new Date());
                          readCountDO.setUpdateTime(new Date());
                          weekendNoticeReadRepositoryService.insert(readCountDO);
                          return true;
                      });
                  } catch (ApiException err) {
                      throw err;
                  } catch (Exception e) {
                      log.error("插入", e);
                      throw new ApiException(ErrorEnum.SERVER_ERROR.getCode(), "服务端出错");
                  }
              } else {
                  weekendNoticeReadRepositoryService.noticeCountAdd(weekendNoticeReadCountDO);
              }
          } else {
              log.warn("redis锁获取超时,key:{}", lockKey);
              throw new ApiException(ErrorEnum.SERVER_ERROR.getCode(), "服务器繁忙,请稍后重试");
          }
      }

      在获取到锁之后,可能是经过等待才获取到的锁,此时上一个释放锁的线程可能已经插入了数据了,所以在锁内部,依旧要再次校验一下数据是否存在。
      这种写法适合大多数需要唯一性的写场景。

      避免死锁

      如何避免死锁?最简单有效的方法就是:**不要在锁里再去获取锁,简而言之就是锁最好单独使用,不要套娃。
      也要注意一些隐性锁,比如数据库。
      事务A:

      • 插入[5,7],插入意向锁。

      • select for update更新[100,150],间隙锁。
        事务B:

      • select for update更新[90,120],间隙锁。

      • 插入[4,6],插入意向锁。

      此时在并发场景下,就可能会出现A持有了[5,7]的间隙锁,在等待事务B[90,120]的间隙锁,事务B也一样,就死锁了。
      **

      顺带谈谈并发场景下常见的问题

      读写混乱

      在写业务代码,定义一些工具类或者缓存类的时候,很容易疏忽而发生类似的问题。
      比如构建一个static缓存,没有使用ConcurrentHashMap中的putIfAbsent等方法,也没有加锁去构建,导致上面的线程刚put了,下面的线程就删掉了,或者重复构建2次缓存。

      Redis或者一些并发操作释放锁或者资源,没有检查是否是当前线程持有

      这点在Redis锁的示例代码也讲到了。
      线程A获取到锁,此时B,C在等待,然后A执行时间过长,导致锁超时被自动释放了,此时B获取到了锁,在快乐的执行,然后A执行完了之后,释放锁时没有判断是否还是自己持有,导致B持有的锁被删除了,此时C又获取到了锁,BC同时在执行。

      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发 Java MySQL
      相关文章 更多
      精品专题 更多
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      macOS软件

      正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

      IOS软件

      正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

      AI

      正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

      Mac软件 更多
      Aperty macOS版
      Aperty macOS版

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      火山引擎 网页版
      火山引擎 网页版

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      立刻MV 网页版
      立刻MV 网页版

      立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

      蛙蛙写作 网页版
      蛙蛙写作 网页版

      蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

      讯飞智作 网页版
      讯飞智作 网页版

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      超级简历WonderCV 网页版
      超级简历WonderCV 网页版

      超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

      音述AI 网页版
      音述AI 网页版

      音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

      AionClaw macOS版
      AionClaw macOS版

      AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

      Loomy macOS版
      Loomy macOS版

      Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

      AirBuddy 3 macOS版
      AirBuddy 3 macOS版

      AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

      AirServer macOS版
      AirServer macOS版

      AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

      AnyDesk macOS版
      AnyDesk macOS版

      AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

      WINDOWS 更多
      讯飞绘文 网页版
      讯飞绘文 网页版

      讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

      Maya(Autodesk Maya) Windows版
      Maya(Autodesk Maya) Windows版

      Autodesk Maya 是面向影视、电视、动画和游戏行业的专业三维制作软件,集多边形与 NURBS 建模、角色绑定、关键帧和动作捕捉动画、Bifrost 特效模拟、材质开发及 Arnold 渲染于一体,

      Aperty Windows版
      Aperty Windows版

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      火山引擎 网页版
      火山引擎 网页版

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      立刻MV 网页版
      立刻MV 网页版

      立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

      蛙蛙写作 网页版
      蛙蛙写作 网页版

      蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

      讯飞智作 网页版
      讯飞智作 网页版

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      超级简历WonderCV 网页版
      超级简历WonderCV 网页版

      超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

      音述AI 网页版
      音述AI 网页版

      音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

      AionClaw Windows版
      AionClaw Windows版

      AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

      Loomy Windows版
      Loomy Windows版

      Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

      AirServer Windows版
      AirServer Windows版

      AirServer Windows版可把电脑变成AirPlay、Google Cast和Miracast无线投屏接收端,适合会议演示、课堂教学及家庭大屏分享。发送设备通常无需另装客户端,并可同时接收和智能排列多个镜像会话。