1. 程式人生 > 其它 >JDBC_07悲觀鎖和樂觀鎖

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);
        }

    }
}

2.3開始演示