当前位置: 首页>编程语言>正文

java执行后锁表怎么处理 java锁表的原因

java执行后锁表怎么处理 java锁表的原因,java执行后锁表怎么处理 java锁表的原因_锁机制,第1张

在JAVA的面试中,不可避免的会遇到一些锁机制的一些问题,尽管这些问题对于编程十分的重要,但是更多的时候,你实际到了这家公司之后,发现这些根本用不上,所谓“面试高大上,工作拧螺丝”,这是一个普遍的现状,我也遇到过。但是没有面试高大上,就没有进去拧螺丝的机会,这些知识还是要掌握的,因此,特地总结了这篇博客,分享给大家,有不足的地方欢迎各位批评指正。

乐观锁和悲观锁

乐观锁总是认为每次去拿数据的时候都认为数据不会被修改,所以不上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。它使用版本号机制和CAS算法实现。

悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别的线程会一直阻塞直到拿到锁。

应用场景:两种锁各有优缺点。乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销。但如果是多写的情况,一般会经常产生冲突,此时用悲观锁就比较合适。

  • 乐观锁代码演示

CAS方式: 数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。(参考下文的CAS算法)

这种方式会引发一个著名的“ABA”问题。 分为两种 即基本数据类型 和引用类型。
怎么理解呢,就是当我操作一个公共数据的时候,可能我通过CAS算法得到的结论是我应该可以更改这个变量,但是这个变量中途可能已经发生了变化,只不过当你再次获取到的时候是符合你的预期的,即没有更改。但是这样的话,基本数据类型的情况好说,如果是引用数据类型,则可能导致问题,因为我操作的始终是一个引用,引用类型的属性的变化不会导致其引用的改变,这就出现了冲突。

加版本号的方式(推荐):

// 创建一个带有version版本字段的表  给一个默认值  这个值只能增加 不能减少
mysql> select * from test;                                                         
+----+-----------+---------+
| id | username  | version |
+----+-----------+---------+
|  1 | zhangsan |   1|
+----+-----------+---------+
1 row in set (0.00 sec)
/*!模拟乐观锁插入 先查到version=1 在更新的时候去校验*/;
mysql> update test set username='zhangsan1',version=version+1 where id=1 and version=2;
Query OK, 1 row affected (0.04 sec)
Rows matched: 1  Changed: 1  Warnings: 0、

/* 乐观锁插入成功*/;
mysql> select * from test;                                                         
+----+-----------+---------+
| id | username  | version |
+----+-----------+---------+
|  1 | zhangsan1 |   2|
+----+-----------+---------+

/*!模拟乐观锁插入 李四(lishi)更改了数据并更新了version=2*/;
mysql> update test set username='lishi',version=version+1 where id=1 and version=1;
Query OK, 1 row affected (0.08 sec)


mysql> select * from test;                                                         
+----+----------+---------+
| id | username | version |
+----+----------+---------+
|  1 | lishi    |  3|
+----+----------+---------+
1 row in set (0.00 sec)

/*!模拟乐观锁插入 张三(zhangsan)尝试更改了数据,version=2,version对不上更新失败*/;
mysql> update test set username='zhangsan2',version=version+1 where id=1 and version=2;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0  Changed: 0  Warnings: 0

mysql> select * from test;                                                         
+----+----------+---------+
| id | username | version |
+----+----------+---------+
|  1 | lishi    |   3|
+----+----------+---------+
1 row in set (0.00 sec)
  • 悲观锁代码演示
/*!0.开始事务 begin;*/;
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
/*!查询提交事务过程中的结果*/;
mysql> select username from test where id = 1 and version=1 for update;
+----------+
| username |
+----------+
| zhangdan|
+----------+
1 row in set (0.01 sec)

/*!修改信息*/;
mysql> update test set username='zhangsan2',version=version+1 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
/*!提交事务*/;
mysql> commit;
Query OK, 0 rows affected (0.04 sec)
/*!查询提交事务成功后的结果*/;
mysql> select * from test;
+----+-----------+---------+
| id | username  | version |
+----+-----------+---------+
|  1 | zhangdan2 |   2|
+----+-----------+---------+
1 row in set (0.00 sec)
  • select…for update的方式,这样就通过开启排他锁的方式实现了悲观锁。此时在test表中,id为1的 那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改

版本号机制

是指对一个表进行更改的时候,A B获取到表后拿到一个version字段1, 然后A修改完数据后B还没有修改完此时表的版本号变成了2,B修改后发现此时版本号已经是2了,和获取表的时候拿到的1不一致,就无法提交修改,只能再次刷新进行更改提交。

CAS算法(compare and swap 比较和交换)

它是一种有名的无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三个操作数

  • 需要读写的内存值 V
  • 进行比较的值 A
  • 拟写入的新值 B

当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。
CAS 使用场景:线程冲突较少的情况下,由于它是基于硬件实现的,不需要切换线程,使用CAS性能更好,但是如果线程较多的情况下,CAS会产生自旋(也就是不成功就一直循环执行直到成功)

Mysql Innodb中的行锁分为共享锁和排他锁(经常用到的锁机制)

共享锁:又叫做读锁,所有的事务只能对其进行读操作不能写操作,加上共享锁后在事务结束之前其他事务只能再加共享锁,除此之外其他任何类型的锁都不能再加了。

用法:

SELECT `id` FROM  table WHERE id in(1,2) LOCK IN SHARE MODE 结果集的数据都会加共享锁

排他锁:

若某个事物对某一行加上了排他锁,只能这个事务对其进行读写,在此事务结束之前,其他事务不能对其进行加任何锁,其他进程可以读取,不能进行写操作,需等待其释放。

用法:

SELECT `id` FROM mk_user WHERE id=1 FOR UPDATE

注意:由于在innodb中行锁,他是针对索引去锁定该条数据,而不是直接锁定该条数据的。

另外,在行锁中,如果没有设置索引,InnoDB只能使用表锁。

Mysql Innodb中的表锁(用到的少)

  • 特点:偏向MyISAM存储引擎,开销小,加锁快;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

我们在编辑表,或者执行修改表的事情了语句的时候,一般都会给表加上表锁,可以避免一些不同步的事情出现,表锁分为两种,一种是读锁,一种是写锁。

我们给表加上读锁和写锁分别会有什么效果呢?

加读锁(共享锁):

  • 我们加读锁的这个进程可以读加读锁的表,但是不能读其他的表。
  • 加读锁的这个进程不能update加读锁的表。
  • 其他进程可以读加读锁的表(因为是共享锁),也可以读其他表
  • 其他进程update加读锁的表会一直处于等待锁的状态,直到锁被释放后才会update成功。

加写锁(独占锁):

  • 加锁进程可以对加锁的表做任何操作(CURD)。
  • 其他进程则不能查询加锁的表,需等待锁释放
  • 小结:读锁会阻塞写,但是不会堵塞读。而写锁则会把读和写都堵塞。



https://www.xamrdz.com/lan/5yg1932506.html

相关文章: