具体的乐观锁定(Java)示例

具体的乐观锁定(Java)示例,java,concurrency,locking,optimistic-locking,Java,Concurrency,Locking,Optimistic Locking,我花了一上午的时间阅读了所有的顶级文章,但就我的一生而言,我仍然没有真正理解它 我知道乐观锁定包括添加一列来跟踪记录的“版本”,该列可以是时间戳、计数器或任何其他版本跟踪构造。但我仍然不理解如何确保写完整性(这意味着如果多个进程同时更新同一个实体,那么之后,该实体将正确地反映它应该处于的真实状态) 有人能提供一个具体的、易于理解的例子来说明乐观锁在Java中的使用方式(可能是针对MySQL数据库)。假设我们有一个人实体: public class Person { private Str

我花了一上午的时间阅读了所有的顶级文章,但就我的一生而言,我仍然没有真正理解它

我知道乐观锁定包括添加一列来跟踪记录的“版本”,该列可以是时间戳、计数器或任何其他版本跟踪构造。但我仍然不理解如何确保写完整性(这意味着如果多个进程同时更新同一个实体,那么之后,该实体将正确地反映它应该处于的真实状态)

有人能提供一个具体的、易于理解的例子来说明乐观锁在Java中的使用方式(可能是针对MySQL数据库)。假设我们有一个
实体:

public class Person {
    private String firstName;
    private String lastName;
    private int age;
    private Color favoriteColor;
}
Person
实例被持久化到
people
MySQL表:

CREATE TABLE people (
    person_id PRIMARY KEY AUTO_INCREMENT,
    first_name VARCHAR(100) NOT NULL,
    last_name VARCHAR(100) NOT NULL,        # } I realize these column defs are not valid but this is just pseudo-code
    age INT NOT NULL,
    color_id FOREIGN KEY (colors) NOT NULL  # Say we also have a colors table and people has a 1:1 relationship with it
);
现在让我们假设有两个软件系统,或者一个系统上有两个线程,它们试图同时更新相同的
Person
实体:

  • 软件/线程#1正在尝试保留姓氏更改(从“John Smith”到“John Doe”)
  • 软件/线程#2正在尝试保持最喜爱颜色的更改(从红色到绿色)
我的问题是:
  • 如何在
    人员
    和/或
    颜色
    表上实现乐观锁定?(寻找特定的DDL示例)
  • 那么,如何在应用程序/Java层利用这种乐观锁定呢?(查找特定的代码示例)
  • 有人能让我经历这样一个场景吗?在这个场景中,DDL/代码更改(来自上面的#1和#2)将在我的场景(或任何其他场景)中发挥作用,并且将正确地“乐观地锁定”
    人员
    /
    颜色
    表?基本上,我希望看到乐观锁定的作用,并简单解释它的工作原理

  • 通常,当您研究乐观锁定时,您也会使用类库或其他支持的JPA实现

    示例可以如下所示:

    公共类人物{
    私有字符串名;
    私有字符串lastName;
    私人互联网;
    私人色彩偏爱色彩;
    @版本
    私人长版;
    }
    
    显然,如果您没有使用支持此功能的框架,那么添加
    @Version
    注释是没有意义的

    然后,DDL可能会被删除

    创建表格人物(
    人员id主键自动递增,
    first_name VARCHAR(100)不为空,
    last_name VARCHAR(100)NOT NULL,#}我知道这些列定义无效,但这只是伪代码
    年龄INT不为空,
    color_id外键(colors)NOT NULL,#假设我们还有一个colors表,并且人们与它有1:1的关系
    版本BIGINT不为空
    );
    
    这个版本会发生什么?
  • 每次存储实体之前,都要检查数据库中存储的版本是否仍然是您知道的版本
  • 如果是,则以版本递增1的方式存储数据
  • 为了完成这两个步骤而不冒其他进程在两个步骤之间更改数据的风险,通常通过如下语句处理

    updateperson SET lastName='marred',version=2,其中Person\u id=42,version=1;
    
    执行语句后,检查是否更新了行。如果您这样做了,那么在您读取数据之后,没有其他人更改数据,否则其他人会更改数据。如果其他人更改了数据,您通常会收到您正在使用的库发出的警告

    此异常应导致撤销所有更改,并重新启动更改值的过程,因为更新实体的条件可能不再适用

    因此,没有碰撞:

  • 处理一个人
  • 进程写入一个人,从而增加版本
  • 进程B读取个人
  • 进程B写入Person,从而增加版本
  • 碰撞:

  • 处理一个人
  • 进程B读取个人
  • 进程写入一个人,从而增加版本
  • 进程B在尝试保存为自读取Person后更改的版本时收到异常
  • 如果颜色是另一个物体,你应该按照同样的方案在那里放置一个版本

    什么是不乐观的?
    • 乐观锁定并不是合并冲突更改的魔法。乐观锁定只会防止进程意外地被另一个进程覆盖更改
    • 乐观锁实际上不是真正的DB锁。它只是通过比较version列的值来工作。您不会阻止其他进程访问任何数据,因此希望您得到
      OptimisticLockException
      s
    使用哪种列类型作为版本? 如果有许多不同的应用程序访问您的数据,最好使用数据库自动更新的列。e、 g.对于MySQL

    版本时间戳更新当前时间戳时的默认当前时间戳;
    
    这样,实现乐观锁定的应用程序将注意到哑应用程序的更改

    如果更新实体的频率高于
    时间戳的分辨率或Java对它的解释,那么这种方法可能无法检测到某些更改。另外,如果让Java生成新的
    时间戳
    ,则需要确保运行应用程序的所有计算机都处于完美的时间同步状态

    如果您的所有应用程序都可以更改为整数、长。。。版本通常是一个很好的解决方案,因为它不会受到不同时钟设置的影响;-)

    还有其他情况。例如,每次更改行时,您可以使用哈希,甚至可以随机生成
    字符串。重要的是,当任何进程为本地处理或缓存内保存数据时,不要重复值,因为该进程将无法通过查看数据来检测更改