详解java中的悲观锁与乐观锁的区别

Java中的悲观锁和乐观锁的区别在于,悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。而乐观锁则是在操作时很乐观,认为操作不会产生并发问题 (不会有其他线程对数据进行修改),因此不会上锁。但是在更新时会判断其他线程在此之前是否已经更新过,如果没有则更新成功并返回新值;否则抛出异常或者回滚 。

悲观锁与乐观锁是并发控制中的两种主要策略,在Java中,这两种锁策略都有各自的实现方式和应用场景,本文将详细介绍悲观锁和乐观锁的概念、原理、实现以及它们在Java中的应用场景。

悲观锁

1、概念

详解java中的悲观锁与乐观锁的区别

悲观锁是一种基于数据完整性约束的并发控制策略,它假设多个事务同时访问同一数据时会发生冲突,因此在事务开始执行之前就对可能受到冲突的数据加锁,以防止其他事务对其进行修改,悲观锁的实现方式主要是通过数据库的排他锁(X锁)来实现的。

2、原理

悲观锁的原理是通过在数据表中添加一个唯一标识符(如主键),当一个事务要访问某个数据时,首先会查询该数据是否被其他事务锁定,如果被锁定,则当前事务需要等待;如果没有被锁定,则当前事务可以对该数据加上排他锁,防止其他事务对其进行修改,这样,在事务提交之前,其他事务无法对受悲观锁保护的数据进行修改,从而保证了数据的完整性和一致性。

3、实现

在Java中,悲观锁可以通过以下几种方式实现:

(1)使用数据库的排他锁(X锁):在SQL语句中使用SELECT ... FOR UPDATELOCK IN SHARE MODE来加锁。

详解java中的悲观锁与乐观锁的区别

SELECT * FROM users WHERE id = 1 FOR UPDATE;

或者

SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;

(2)使用Java的JDBC API:通过调用Connection对象的setAutoCommit(false)方法关闭自动提交,然后调用PreparedStatement对象的executeQuery()方法执行查询并加上排他锁,调用Connection对象的commit()方法提交事务。

Connection conn = null;
PreparedStatement pstmt = null;
try {
    conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
    conn.setAutoCommit(false);
    pstmt = conn.prepareStatement("SELECT * FROM users WHERE id = ? FOR UPDATE");
    pstmt.setInt(1, 1);
    ResultSet rs = pstmt.executeQuery();
    // 对查询结果进行处理
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    if (conn != null) {
        try {
            conn.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            conn.close();
        }
    }
}

乐观锁

1、概念

乐观锁是一种基于版本号的并发控制策略,它假设多个事务在同一时间访问同一数据时不会发生冲突,因此不需要在事务开始执行之前就对可能受到冲突的数据加锁,乐观锁的实现方式主要是通过对数据表中每个记录的版本号字段进行更新来实现的,当一个事务要更新某个数据时,会检查该数据的版本号是否与自己读取到的版本号一致,如果一致,则更新数据并将版本号加一;如果不一致,则说明其他事务已经修改了该数据,此时可以选择重新获取数据或抛出异常,这样,在事务提交之前,其他事务只能看到已提交的数据版本,从而避免了数据的不一致性。

2、原理

乐观锁的原理是通过为每个数据记录添加一个版本号字段,并在每次更新数据时递增版本号,当一个事务要更新某个数据时,会先查询该数据的当前版本号,然后将自己的版本号设置为该值,接下来,事务会再次查询该数据的新版本号,如果新版本号与自己的版本号一致,则更新数据并将版本号加一;如果新版本号大于自己的版本号,则说明其他事务已经修改了该数据,此时可以选择重新获取数据或抛出异常,这样,在事务提交之前,其他事务只能看到已提交的数据版本,从而避免了数据的不一致性。

详解java中的悲观锁与乐观锁的区别

3、实现

在Java中,乐观锁可以通过以下几种方式实现:

(1)使用数据库的支持:许多关系型数据库都支持乐观锁机制,例如InnoDB和MyISAM等,这些数据库通常会在插入或更新数据时自动生成一个唯一的行ID(如MySQL的AUTO_INCREMENT),并将其作为版本号。

INSERT INTO users (name, age) VALUES ('张三', 25) ON DUPLICATE KEY UPDATE name='李四', age=26; -如果插入成功,则不返回任何值;如果插入失败(因为id重复),则返回1;如果更新成功(且没有其他事务修改该数据),则返回0;如果更新失败(或有其他事务修改该数据),则返回2;如果更新失败(但有其他事务插入了相同的id),则返回3。

(2)使用Java的JDBC API:通过调用PreparedStatement对象的executeUpdate()方法执行更新操作。

Connection conn = null;
PreparedStatement pstmt = null;
try {
    conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
    int affectedRows = pstmt = conn.prepareStatement("INSERT INTO users (name, age) VALUES (?, ?) ON DUPLICATE KEY UPDATE name=?, age=?") // 如果插入成功,则不返回任何值;如果插入失败(因为id重复),则返回1;如果更新成功(且没有其他事务修改该数据),则返回0;如果更新失败(或有其他事务修改该数据),则返回2;如果更新失败(但有其他事务插入了相同的id),则返回3,pstmt.setString(1, "张三"); pstmt.setInt(2, 25); pstmt.setString(3, "李四"); pstmt.setInt(4, 26); affectedRows = pstmt.executeUpdate(); // affectedRows为受影响的行数或相应的错误代码。
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (affectedRows == SQLErrorCode.ER_DUP_ENTRY_ON_INDEX) { // 如果受影响的行数为2或3,则表示发生了行级锁定或死锁,可以在应用程序中根据具体情况选择重试或终止操作。
            } else if (affectedRows == SQLErrorCode.ER_ROW_IS_REFERENCED_BY_OTHER_TABLE) { // 如果受影响的行数为4或5,则表示发生了外键约束冲突,可以在应用程序中根据具体情况选择重试或终止操作。
            } else if (affectedRows == SQLErrorCode.ER_NO_SUCH_TABLE || affectedRows == SQLErrorCode.ER_BAD_NULL_ERROR || affectedRows == SQLErrorCode.ER_ACCESS_DENIED || affectedRows == SQLErrorCode.ER_HOST_IS_BLOCKED) { // 如果受影响的行数为0、1、7或8,则表示发生了连接错误或其他未知错误,可以在应用程序中根据具体情况选择重试或终止操作。
            } else if (affectedRows == SQLErrorCode.ACQUIRED) { // 如果受影响的行数为9或10,则表示发生了死锁,可以在应用程序中根据具体情况选择重试或终止操作,else if (affectedRows == SQLErrorCode.COMMITTED) { // 如果受影响的行数为11或12,则表示更新成功且没有其他事务修改该数据,可以在应用程序中根据具体情况选择继续操作或提交事务,else if (affectedRows == SQLErrorCode.ROLLBACKED || affectedRows == SQLErrorCode.UNKNOWN) { // 如果受影响的行数为13或14,则表示更新失败或发生了未知错误,可以在应用程序中根据具体情况选择重试或回滚事务,else if (affectedRows == SQLErrorCode.SERIALIZATION_FAILED || affectedRows == SQLErrorCode.GENERIC_FAILURE || affectedRows == SQLErrorCode.DATA_READONLY || affectedRows == SQLErrorCode.DATA_WRONG_TYPE || affectedRows == SQLErrorCode.FEATURE_NOT_SUPPORTED || affectedRows == SQLErrorCode.CONNECTION_DOES_NOT_EXIST || affectedRows == SQLErrorCode.CONNECTION_FAILURE || affectedRows == SQLErrorCode.CONNECTION_BROKEN || affectedRows == SQLErrorCode.TRANSACTION_CONFLICT || affectedRows == SQLErrorCode.TRANSACTION_UNCOMPLETED || affectedRows == SQLErrorCode.TRANS

原创文章,作者:K-seo,如若转载,请注明出处:https://www.kdun.cn/ask/261386.html

Like (0)
Donate 微信扫一扫 微信扫一扫
K-seo的头像K-seoSEO优化员
Previous 2024-01-25 11:16
Next 2024-01-25 11:18

相关推荐

  • 深入分析mysql事务与存储引擎的底层结构

    MySQL事务基于ACID模型,存储引擎负责数据读写。InnoDB通过MVCC实现并发控制,MyISAM依赖表级锁。

    2024-05-23
    085
  • sqlserver设置事务隔离级别

    SQL Server的事务操作隔离模式是数据库管理系统中的一个重要概念,它用于控制并发事务之间的相互影响,确保数据的一致性和完整性,在SQL Server中,事务隔离级别定义了事务与其他事务交互的可见性和行为,本文将详细介绍SQL Server中的事务隔离模式及其相关技术。1、事务隔离的基本概念事务隔离是为了解决多个用户同时访问数据库……

    2024-03-13
    0157
  • Redis事务涉及的watch、multi等命令详解

    Redis是一个开源的使用ANSI C编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API,它常被用作数据库、缓存和消息中间件,在Redis中,事务是一组命令的集合,这些命令要么全部执行,要么全部不执行。1. Redis事务的基本概念Redis事务主要涉及以下三个命令:MULTI:标记一个事务……

    2024-03-09
    0209
  • innodb事务实现原理是什么

    InnoDB事务实现原理是什么InnoDB是MySQL的一种存储引擎,它提供了ACID(原子性、一致性、隔离性和持久性)事务支持,本文将详细介绍InnoDB事务的实现原理。1. InnoDB事务的基本概念在开始之前,我们首先需要理解什么是事务以及InnoDB中的事务是如何工作的。事务是一系列对数据库的操作序列,这些操作要么全部成功,要……

    2023-12-20
    0104
  • python django事务实例源码解析

    Python Django事务实例源码解析在Django中,事务是确保数据一致性的重要手段,当一个事务中的操作出现错误时,可以通过回滚(rollback)来撤销这些操作,从而保证数据的完整性,本文将通过一个简单的实例来解析Django事务的实现原理。我们需要了解Django中的两个主要概念:数据库连接(database connect……

    2024-01-03
    0120
  • oracle数据更改后出错的解决方法有哪些

    Oracle数据更改后出错是很常见的问题,可能的原因有很多,例如权限问题、事务问题、索引问题等,本文将详细介绍如何解决这些问题。权限问题1、1 用户权限不足当用户没有足够的权限来修改数据时,会抛出异常,解决方法是为用户分配足够的权限,可以使用以下SQL语句为用户分配权限:GRANT UPDATE (column_name) ON ta……

    2024-03-17
    0133

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

免备案 高防CDN 无视CC/DDOS攻击 限时秒杀,10元即可体验  (专业解决各类攻击)>>点击进入