先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
正文
3、获取锁,过程如下
{
3.1、开启事务
3.2、int count = (update t_read_write_lock set w_count=1 where r_count = 0)
3.3、提交事务;
}
4、如果3.2的count==1,继续向下执行,否则终止业务
5、执行业务操作
6、释放锁,过程如下
{
6.1、开启事务
6.2、update t_read_write_lock set w_count=0 where w_count = 1
6.3、提交事务
}
整个过程有个问题,不知道大家发现没有,如果执行到 5 之后,系统挂了,会出现什么情况?
业务执行完毕了,但是 w 锁却没有释放,这种后果就是死锁了,以后 r 操作就没法执行了。
我们来看看,如何改进?
需要添加一下上锁日志表,每次上锁成功,则记录一条日志,表结构如下
create table t_lock_log(
id bigint primary key auto_increment comment ‘主键,自动增长’
resource_id varchar(50) primary key not null comment ‘互斥资源id’,
lock_type smallint default 0 comment ‘锁类型,0:W锁,1:R锁’,
status smallint default 0 comment ‘状态,0:获取锁成功,1:业务执行完毕,2:锁被释放’,
create_time bigint default 0 comment ‘记录创建时间’,
version bigint not null default 0 comment ‘版本号,每次执行update的时候+1’
);
如何使用呢?
下面看 W 过程的改进
1、通过resource_id去t_read_write_lock查询,如果不存在,则插入一条记录,这里由于resource_id是主键,所以对于同一个resource_id只会有一个插入成功,这里用 $lock_record表示t_read_write_lock记录
2、判断lock_record.w_count 0 && lock_record.r_count0,如果为true继续向下,否则返回false,业务终止
3、获取锁,过程如下
{
3.1、开启事务
3.2、int count = (update t_read_write_lock set w_count=1 where r_count = 0)
3.3、如果count==1,则插入一条上锁日志,锁类型是0,状态是0:insert t_lock_log (resource_id,lock_type,status,create_time) values (#{resource_id},0,0,‘当前时间’);
3.4、提交事务;
}
4、如果3.2的count==1,继续向下执行,否则终止业务
5、执行业务操作,业务操作过程如下
{
5.1、业务库开启事务
5.2、执行业务
5.3、更新锁日志记录的状态为1,条件中必须带上status=0:int updateLogCount = (update t_lock_log set status=1 where id=#{日志记录id} and status = 0)
5.4、if(updateLogCount==1){
5.5、提交事务
}else{
5.6、回滚事务【走到这里说明更新锁日志记录失败了,说明t_lock_log的status被其他地方改掉了,被防止死锁的job修改了】
}
}
6、释放锁,过程如下
{
6.1、开启事务
6.2、释放锁:update t_read_write_lock set w_count=0 where w_count = 1 and resource_id = #{resource_id}
6.3、更新锁日志状态为2:update t_lock_log set status=2 where id = #{日志记录id}
6.4、提交事务
}
2.3、死锁的处理
上面这个是正常流程,如果第 3 步执行完了,也就是上锁 W 锁成功,但是执行到第 6 步之前,系统挂了,此时 W 锁没有释放,会出现死锁。
此时我们需要一个 job,通过这个 job 来释放长时间还未释放的锁,比如过了 10 分钟,锁还未被释放的,job 的逻辑如下
1、获取10分钟之前锁未释放的锁日志列表:select * from t_lock_log where status in (0,1) and create_time+10分钟<=当前时
间的;
2、轮询获取的日志列表,释放锁,操作如下
{
2.1、开启事务
2.2、if(t_lock_log.lock_type==0){
//lock_type为0表示是W锁,下面准备释放W锁
//先将日志状态更新为2,注意条件中带上version作为条件,这里使用到了乐观锁,可以确保并发修改时只有一个count的值为1
int count = (update t_lock_log set status=2 where id = #{日志记录id} and version = #{日志记录.version})
if(count==1){
//将w_count置为0
update t_read_write_lock set w_count=0 where w_count = 1 and resource_id = #{resource_id}
}
}else{
//准备释放R锁
//先将日志状态置为2
int count = (update t_lock_log set status=2 where id = #{日志记录id} and version = #{日志记录.version})
if(count==1){
//将r_count置为r_count-1,注意条件中带上r_count - 1>=0
update t_read_write_lock set r_count=r_count-1 where r_count - 1>=0 and resource_id = #{resource_id}
}
}
2.3、提交事务
}
2.4、R 锁的过程
1、通过resource_id去t_read_write_lock查询,如果不存在,则插入一条记录,这里由于resource_id是主键,所以对于同一个resource_id只会有一个插入成功,这里用 $lock_record表示t_read_write_lock记录
2、判断lock_record.w_count ==0,如果为true继续向下,否则返回false,业务终止
3、获取锁,过程如下
{
3.1、开启事务
3.2、int count = (update t_read_write_lock set r_count=r_count+1 where w_count = 0)
3.3、如果count==1,则插入一条上锁日志,锁类型是1【表示R锁】,状态是0:insert t_lock_log (resource_id,lock_type,status,create_time) values (#{resource_id},1,0,‘当前时间’);
3.4、提交事务;
}
4、如果3.2的count==1,继续向下执行,否则终止业务
5、执行业务操作,业务操作过程如下
{
5.1、业务库开启事务
5.2、执行业务
5.3、更新锁日志记录的状态为1,条件中必须带上status=0:int updateLogCount = (update t_lock_log set status=1 where id=#{日志记录id} and status = 0)
5.4、if(updateLogCount==1){
5.5、提交事务
}else{
5.6、回滚事务【走到这里说明更新锁日志记录失败了,说明t_lock_log的status被其他地方改掉了,被防止死锁的job修改了】
}
}
6、释放锁,过程如下
{
6.1、开启事务
6.2、释放锁:update t_read_write_lock set r_count=r_count-1 where r_count - 1 >= 0 and resource_id = #{resource_id}
6.3、更新锁日志状态为2:update t_lock_log set status=2 where id = #{日志记录id}
6.4、提交事务
}
3、总结
本文主要介绍了如何使用 mysql 来实现读写锁,如何防止死锁,重点就是 2 张表,锁表和日志表,2 个表配合一个 job,就把问题解决了。
大家可以将上面代码转换为程序,结合 spring 的 aop 可以实现一个通用的 db 读写锁,有兴趣的可以试试,有问题欢加我微信 itsoku 交流。
4、Spring 系列
-
Spring 系列第 18 篇:@ComponentScan、@ComponentScans 详解(bean 批量注册)
-
Spring 系列第 21 篇:注解实现依赖注入(@Autowired、@Resource、@Primary、@Qulifier)
-
Spring 系列第 29 篇:BeanFactory 扩展(BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor)
最后
权威指南-第一本Docker书
引领完成Docker的安装、部署、管理和扩展,让其经历从测试到生产的整个开发生命周期,深入了解Docker适用于什么场景。并且这本Docker的学习权威指南介绍了其组件的基础知识,然后用Docker构建容器和服务来完成各种任务:利用Docker为新项目建立测试环境,演示如何使用持续集成的工作流集成Docker,如何构建应用程序服务和平台,如何使用Docker的API,如何扩展Docker。
总共包含了:简介、安装Docker、Docker入门、使用Docker镜像和仓库、在测试中使用Docker、使用Docker构建服务、使用Fig编配Docke、使用Docker API、获得帮助和对Docker进行改进等9个章节的知识。
关于阿里内部都在强烈推荐使用的“K8S+Docker学习指南”—《深入浅出Kubernetes:理论+实战》、《权威指南-第一本Docker书》,看完之后两个字形容,爱了爱了!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
1)]
[外链图片转存中…(img-ldzgy3Xy-1713383554701)]
[外链图片转存中…(img-O5yaidjg-1713383554701)]
[外链图片转存中…(img-pgayBQw3-1713383554702)]
关于阿里内部都在强烈推荐使用的“K8S+Docker学习指南”—《深入浅出Kubernetes:理论+实战》、《权威指南-第一本Docker书》,看完之后两个字形容,爱了爱了!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-yM07EKAh-1713383554702)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!