
课程咨询: 400-996-5531 / 投诉建议: 400-111-8989
认真做教育 专心促就业
锁的应用在许多软件开发项目中都得到广泛的应用,而本文我们就通过案例分析来简单了解一下,Java编程锁的实现与应用分析。
锁的实现
锁在计算机底层的实现,依赖于CPU提供的CAS指令(compareandswsp),对于一个内存地址,会比较原值以及尝试去修改的值,通过值是否修改成功,来表示是否强占到了这个锁。
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现在的特点是:
是在javaapi层面实现的锁,所以可以实现各种并发工具类,操作也更加灵活
因为提供了超时时间等机制,操作灵活,所以不易死锁。(相同的,如果发生死锁,将更难排查,因为jstack里将不会有deadlock标识)。
可以实现公平锁,而synchronized必定是非公平锁。
因为是JavaApi层实现的锁,所以可以响应中断。
到这里你会发现,其实ReentrantLock可以说是synchronized在JavaApi层的实现。
Mysql锁
共享锁(S)与排它锁(X)
作用范围
这两种锁都包括行级锁和表级锁。
获取共享锁时,如果该数据被其他事务的排它锁锁住,则无法获取,需要等待排它锁释放。
意向锁
作用范围
意向锁为表锁,在获取表锁之前,一定会检查意向锁。
意图锁定协议如下:
在事务获得表中某行的共享锁之前,它必须先获得表上的IS锁或更强的锁。
在事务获得表中行的排他锁之前,它必须先获得表的IX锁。
在获取任意表锁的共享锁或排它锁之前,一定会检查该表上的共享锁。
意向锁的作用在于:在获取表锁时,可以通过意向锁来快速判断能否获取。
因为获取行级锁时,会先获取对应的意向锁,这样另外的事务在获取表锁时就可以通过意向锁快速的判断,而不需要每行去扫描。
特别注意的是,意向锁是可以叠加的,即会存在多个,如T1事务获取了意向锁IX1和行级锁X1,T2事务依旧可以获取意向锁IX2和行级锁X2,所以仅在获取表级锁之前,才会检查意向锁。
记录锁
记录锁生效在索引上,用以在SELECTc1FROMtWHEREc1=10FORUPDATE时保护该行数据不被其他事务更改。
记录锁在没有索引时依旧会生效,因为innodb会为每张表创建一个隐藏的索引。
记录锁是基本的行锁。
间隙锁
间隙锁生效在索引上,用于锁定索引值后的行,防止插入,在selectfromtablewhereindex=?forupdate时会生效,例如index=1,则会锁住index=1索引节点相关的行,防止其他事务插入数据。
【免责声明】:本内容转载于网络,转载目的在于传递信息。文章内容为作者个人意见,本平台对文中陈述、观点保持中立,不对所包含内容的准确性、可靠性与完整性提供形式地保证。请读者仅作参考。更多内容请加抖音太原达内IT培训学习了解。