- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
在计算机程序中锁用于独占资源,获取到锁才可以操作对应的资源.
锁在计算机底层的实现,依赖于CPU提供的CAS指令(compare and swsp),对于一个内存地址,会比较原值以及尝试去修改的值,通过值是否修改成功,来表示是否强占到了这个锁.
jvm中,有2个常用的锁 。
synchronized是java提供的关键字锁,可以锁对象,类,方法。 在JDK1.6以后,对synchronized进行了优化,增加了偏向锁和轻量锁模式,现在synchronized锁的运行逻辑如下:
很显然,偏向锁设计的目的是“在Java官方看来,对同一个锁的争抢大部分都发生在同个线程上”。 轻量锁设计的目的是“在短期内,锁的争抢通过自旋CAS就可以获取到,短时间内的CPU自旋消耗小于线程挂起再唤醒的消耗”。 重量锁就是最初优化前的synchronized的逻辑了.
说到ReentrantLock,就不得不说到JUC里的AQS了。 AQS全称AbstractQueueSynchronizer,几乎JUC里所有的工具类,都依赖AQS实现。 AQS在java里,是一个抽象类,但是本质上是一种思路在java中的实现而已。 AQS的实现逻辑如下:
在synchronized锁优化以后,AQS的本质与synchronized并没有太大不同,两者的性能也并没有太大差距了,所以AQS现在的特点是:
到这里你会发现,其实ReentrantLock可以说是synchronized在JavaApi层的实现.
这两种锁都包括行级锁和表级锁。 获取共享锁时,如果该数据被其他事务的排它锁锁住,则无法获取,需要等待排它锁释放.
意向锁为表锁 ,在获取表锁之前,一定会检查意向锁.
意图锁定协议如下:
在事务获得表中某行的共享锁之前,它必须首先获得表上的 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的数据不存在.
这个锁是记录锁和间隙锁的组合,简而言之在select from table where index=? for update时,既会有间隙锁防止insert,也会有记录锁在index上防止这一条数据的update和delete。这个Next-key只是对这两种锁的一种概括,因为这两种锁在select for update时通常会一起出现.
插入意向锁,和意向锁类似。不过是特殊的间隙锁,并不发生在select for update,而是在同时发生insert时产生,例如在两个事务同时insert索引区间为[4,7]时,同时获得该区间的意向锁,此时事务不会阻塞,例如A:insert-5,B:insert-7,此时不会阻塞两个事务.
插入意向锁是一个特殊的间隙锁,是为了防止正常间隙锁锁区间的情况下,insert频繁阻塞而设计的,例如A:insert-5,B:insert-7,如果没有插入意向锁,那么5和7都要去尝试获取间隙锁,此时第二个事务就会被阻塞,但是通过插入意向锁,第二个事务就不会被阻塞,只有到插入的行确实冲突,才会被阻塞.
自增锁,这个锁很明显是表级insert锁,为了保证自增主键的表的主键保持原子自增.
对于锁这个东西,大家应该多去理解各种锁设计运行的原理和模型,这样在加深理解后,在使用起来才会更加深入和透彻.
众所周知,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:
此时在并发场景下,就可能会出现A持有了[5,7]的间隙锁,在等待事务B[90,120]的间隙锁,事务B也一样,就死锁了。 ** 。
在写业务代码,定义一些工具类或者缓存类的时候,很容易疏忽而发生类似的问题。 比如构建一个static缓存,没有使用ConcurrentHashMap中的putIfAbsent等方法,也没有加锁去构建,导致上面的线程刚put了,下面的线程就删掉了,或者重复构建2次缓存.
这点在Redis锁的示例代码也讲到了。 线程A获取到锁,此时B,C在等待,然后A执行时间过长,导致锁超时被自动释放了,此时B获取到了锁,在快乐的执行,然后A执行完了之后,释放锁时没有判断是否还是自己持有,导致B持有的锁被删除了,此时C又获取到了锁,BC同时在执行.
最后此篇关于Java与Mysql锁相关知识总结的文章就讲到这里了,如果你想了解更多关于Java与Mysql锁相关知识总结的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
第一段代码工作正常,并给出了我需要的结果。我现在想做的是让它在 'as num' 上返回 3 个数字值对于“as num”上的 3 个不同值,对于同一列上的 3 个不同位置 SELEC
我想分析一些数据以编写定价算法。以下日期可用: 我需要三个变量/维度的函数/相关因子,它显示三个维度(pers_capacity、卧室数量、浴室数量)增长时中位数(价格)的变化。例如Y(#pers_c
正如标题所说 - 我的 Sprite Kit 游戏时不时地在后台崩溃,总是出现此错误 - Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Sub
假设我尝试保存以下数据,并且Songs模型的name属性上设置了Phalcon \ Mvc \ Model \ Validator \ PresenceOf验证器 // Get an existing
我有一个 if 控件,如下所示; if (Directory.Exists(System.IO.Path.Combine(systemPath, "Reports", companyName))
有人可以告诉我我们使用 ReadLine() 从文件 (.txt) 中读取特定行吗?现在我想读取文件的全部内容(不仅仅是第一行)。为此我需要使用什么方法。我用谷歌搜索了很多,但找不到解决方案。 我的代
我相信在大学时我用从 C 派生的语言为 FPGA 编写了一个程序。我了解 VHDL 和 verilog 等语言。但是,我不明白的是程序员在使用哪个方面有多少选择?它依赖于FPGA吗?我将使用 Xili
我有一个 if 控件,如下所示; if (Directory.Exists(System.IO.Path.Combine(systemPath, "Reports", companyName))
如何在运行时更改 Dashcode (Javascript) 中图像对象的源? 我试过: var image = document.getElementById("image").object;ima
我有几个相互关联的类,它们将被多种不同的算法使用 例子: struct B; struct A { B* parent; }; struct B { std::vector child
我正在开发一个网站,用户在客户收到的表中输入金额,如果任何客户没有提供分期付款(金额),则用户不会在表中输入任何金额,并且用户希望获取违约者的信息客户以10天为基础。所以我的问题是应该定义什么表和属性
我试图从上一个条目中选择一个值,并每次将该数字加一。我让它工作到选择当前条目值(默认 1000)并递增 1 并重新插入该值(因此每次最终都是 1001)。我需要它来选择该字段的最后一个条目,这样它将变
我不擅长“制作”查询。假设这是我的数据库: artist pics ------------------- -
最近,我要为我的网站做一个即时通知系统。我听说 COMET 在这种情况下必不可少。 我已经搜索 PHP 和 Comet 一段时间了,但是,我发现的指南和文章似乎只是循环中的 ajax 请求。例如,有一
我正在开发一款 iOS 游戏,我希望 clown 在场景外生成,然后向下移动。我的想法是全部创建它们,并将它们以 360 像素的距离放置在不可见的场景中。 像这样: SKSpriteNode *clo
我有以下子订单表。 mysql> select * from suborder; +-------------+------------------+ | order_state | bookin
这可能是一个有点初学者的问题,但考虑到在 Java 中调试编码是相当相关的:什么时候编码与 String 对象相关? 假设我有一个要保存到文件中的字符串对象。 String 对象本身是否使用某种我应该
首先我想说我是 CPP 的新手(我从 cpp11 开始):)考虑以下实体:学生(名字+姓氏)和组(描述+更多学生)。我在 C++ 中创建了以下 2 个类: class Student { privat
我正在尝试在单击该复选框时同步更新我的数据库。我决定使用 aJax,但它似乎无法识别 ajax。 代码:将成为 Switch_Active(this.id) 函数的元素 ... Deactivat
我正在创建一个菜单。菜单如下。 $('.category').mouseover(function() { $(this).removeClass('category').addClass('cate
我是一名优秀的程序员,十分优秀!