JDBC_07悲观锁和乐观锁
JDBC_07悲观锁和乐观锁
1.悲观锁和乐观锁的概念
1.1悲观锁
悲观锁就是比较悲观。我在拿一条记录时,生怕别人修改这条记录,于是就把它锁住,不让别人用。直到我使用完才解锁。
悲观锁的语法格式是:
select ... from ... where ... for update;
如:
select empno,ename,job,sal from emp where empno = 7499 for update;
在mysql中,当使用for update语句时,决定行锁还是表锁的是条件字段是否能使用索引(如主键、unique等)。
如果能则为行锁,不能则为表锁。
所以在使用悲观锁时,条件字段最好为主键或有unique约束的字段,锁别的可能会将整张表都锁住,导致程序执行效率降低。
1.2乐观锁
乐观锁就是比较乐观,当我要修改一条记录时,不怕被别人修改,所有人都可以拿来用。
当我修改完后,在提交前,会再看一眼这条记录的版本号:
- 如果和被我修改之前的版本号一样,我就将修改的数据提交,然后更改版本号。
- 如果和被我修改之前的版本号不一样,我就将我的操作回滚。
2.演示悲观锁机制
2.1用到的表
emp:
+--------+-----------+---------+
| ename | job | sal |
+--------+-----------+---------+
| SMITH | CLERK | 1800.00 |
| ALLEN | SALESMAN | 1600.00 |
| WARD | SALESMAN | 1250.00 |
| JONES | MANAGER | 4975.00 |
| MARTIN | SALESMAN | 1250.00 |
| BLAKE | MANAGER | 4850.00 |
| CLARK | MANAGER | 4450.00 |
| SCOTT | ANALYST | 3000.00 |
| KING | PRESIDENT | 5000.00 |
| TURNER | SALESMAN | 1500.00 |
| ADAMS | CLERK | 2100.00 |
| JAMES | CLERK | 1950.00 |
| FORD | ANALYST | 3000.00 |
| MILLER | CLERK | 2300.00 |
+--------+-----------+---------+
2.2演示流程
编写程序A和程序B,分别代表事务A和事务B。
事务A执行:
select ename,job,sal from emp where job = 'MANAGER' for update;
事务B执行:
update emp set sal = sal + 1000 where job = 'MANAGER';
在程序A中,在提交事务处打一个断点,然后debug,让事务A停住,
此时,运行程序B,查看程序B的反应。
2.3程序源码
2.3.1程序A
package com.tsccg.jdbc.transaction;
import com.tsccg.jdbc.util.JdbcUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @Author: TSCCG
* @Date: 2021/07/29 14:31
* 演示悲观锁
* 事务A
*/
public class TransactionDemo03 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.connect();
//开启事务
conn.setAutoCommit(false);
//使用悲观锁锁住job为MANAGER的所有记录
String sql = "select ename,job,sal from emp where job = ? for update";
ps = conn.prepareStatement(sql);
ps.setString(1,"MANAGER");
rs = ps.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("ename") + ","
+ rs.getString("job") + ","
+ rs.getDouble("sal"));
}
//提交事务(事务结束)
conn.commit();
} catch (SQLException throwables) {
if (conn != null) {
try {
//回滚事务(事务结束)
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
throwables.printStackTrace();
} finally {
JdbcUtil.close(conn,ps,rs);
}
}
}
2.3.2程序B
package com.tsccg.jdbc.transaction;
import com.tsccg.jdbc.util.JdbcUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @Author: TSCCG
* @Date: 2021/07/29 14:41
* 演示悲观锁
* 事务B
*/
public class TransactionDemo04 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.connect();
conn.setAutoCommit(false);
//使用悲观锁锁住job为MANAGER的所有记录
String sql = "update emp set sal = sal + 1000 where job = ?";
ps = conn.prepareStatement(sql);
ps.setString(1,"MANAGER");
int count = ps.executeUpdate();
System.out.println(count > 0 ? "修改成功" : "修改失败");
conn.commit();
} catch (SQLException throwables) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
throwables.printStackTrace();
} finally {
JdbcUtil.close(conn,ps,null);
}
}
}