欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

行级锁(悲观锁)的概念

程序员文章站 2024-01-13 15:26:34
...

行级锁(悲观锁)的概念:

怎么样才会产生行级锁?
例如:select ename, job, sal from emp where job = ‘Manager’ for update;
在后面加了for update就产生了行级锁。

行级锁有什么用?
将查询结果集在表中的对应的记录,开始锁住,在当前事务结束前,别的事务根本修改不了锁住的数据。

行级锁也被称为悲观锁。
有悲观锁,就对应有乐观锁,那什么是乐观锁?
乐观锁会在数据后面添加一个隐藏的版本号,假设为version。
只要事务读取到的版本号没有改变,事务就能对数据进行修改。

例如:
假设有两个事务对表中的同一数据进行修改。
事务A:读取到版本号为1.1。
事务B:读取到的版本为1.1。
事务A先修改了数据,并对应的把版本号修改为1.2。
事务B再去修改数据,修改之后发现版本号变成了1.2,与原来读取到的版本不一致,放弃修改,事务回滚。

悲观锁(行级锁):事务必须排队执行,数据被锁住,无法并发执行。
乐观锁:支持并发,事务不需要排队,只不过需要版本号一致。


(1)Test01和Test02共同演示行级锁:
Test01开启一个事务,进行查询,并锁住相应的数据。

测试代码:

import jdbc_util.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Test01 {
    public static void main(String[] args) {

        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            // 1、注册驱动,2、获取连接
            conn = DBUtil.getConnection();
            // 关闭事务自动提交,开启一个事务
            conn.setAutoCommit(false);

            // 3、获取预编译数据库操作对象
            String sql = "select ename, job, sal from emp where job = ? for update";
            ps = conn.prepareStatement(sql);

            // 给占位符传值
            ps.setString(1, "Manager");

            // 4、执行sql语句
            rs = ps.executeQuery();

            // 将断点打在这,也就是提交事务之前,看Test02运行能否修改锁住的数据
            // 5、处理查询结果集
            while (rs.next()) {
                String ename = rs.getString("ename");
                String job = rs.getString("job");
                String sal = rs.getString("sal");
                System.out.println(ename + "-" + job + "-" + sal);
            }


            // 手动提交事务,当前事务结束
            conn.commit();

        } catch (SQLException throwables) {
            if (conn != null) {
                try {
                    conn.rollback();  // 出现异常,事务回滚
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            throwables.printStackTrace();
        } finally {
            // 6、释放资源
            DBUtil.close(conn, ps, rs);
        }
    }
}

(2)Test02程序开启一个事务,去修改被Test01用行级锁(悲观锁)锁住的数据:

测试代码:

import jdbc_util.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Test02 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            // 1、注册驱动,2、获取连接
            conn = DBUtil.getConnection();
            // 关闭事务自动提交,开启一个事务
            conn.setAutoCommit(false);

            // 3、获取预编译的数据库操作对象
            String sql = "update emp set sal = sal + 10 where job = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, "Manager");

            // 4、执行sql语句
            int line = ps.executeUpdate();
            System.out.println(line);

            // 5、无处理查询结果集
            // 手动提交事务,当前事务结束
            conn.commit();

        } catch (SQLException throwables) {
            if (conn != null) {
                try {
                    conn.rollback();  // 出现异常,事务回滚
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            throwables.printStackTrace();
        } finally {
            // 6、释放资源
            DBUtil.close(conn, ps, null);
        }
    }
}

测试结果:
Test01事务结束之前,Test02事务无法修改被Test01用行级锁锁住的数据。