2

我正在@Stateless休息资源中的 WildFly 9.0.2 上实现不可重复读取隔离级别

  1. 线程 A 正在读取一个Account实体,打印余额,然后做一些其他工作(睡眠)。
  2. 线程 B 进来并读取相同的Account实体,打印余额并通过以下方式计算余额calculateBalance()方法计算余额,然后更新实体。它再次读取实体并打印出余额。
  3. 然后线程 A 读取实体并打印出余额。

根据我对不可重复读取级别的理解,线程 B 应该阻塞,直到线程 A 完全完成(退出事务/无状态休息资源)。

这是打印输出:

  • 线程 A:printBalance=500
  • 线程 B:printBalance=500
  • 线程 B:printBalance=600
  • 线程 A:printBalance=500

从那里我可以看到线程 B 没有阻塞,即使线程 A 仍然很忙,它也被允许运行。

下面是代码:

    @GET
    @Path("/{accountId}/{threadName}")
    public Response calculcateBalance(@PathParam("accountId") Long accountId, @PathParam("threadName") String threadName) {

        Account account = em.find(Account.class, accountId);
        printBalance(account,threadName);

        if ("ThreadA".equals(threadName)) {
            sleepSeconds(10);
        } else if ("ThreadB".equals(threadName)) {
            account.calculateBalance();
            em.merge(account);
        }

    account = em.find(Account.class, accountId);
    printBalance(account,threadName);

    return Response.ok().build();
}

如果我将隔离级别更改为可序列化,一切都会阻塞。

我对不可重复阅读的理解是错误的吗?线程 B 是否应该在线程 A 完成之前不被阻塞?

4

1 回答 1

2

这取决于底层数据库系统。如果您使用默认使用 2PL 的 SQL Server,线程 A 将在读取该行时获取共享锁,而线程 B 将阻止写入该行(直到线程 A 释放共享锁)。

Oracle、PostgreSQL 和 MySQL 使用 MVCC,而可重复读取不使用锁定,因为读取器不会阻塞写入器,写入器不会阻塞读取器。在 MVCC 中,检测到异常,如果线程 B 修改了该行,那么线程 A 将检测到该更改并中止其事务。

因此,在 MVCC 中,异常被检测而不是被阻止

于 2016-01-22T16:59:59.147 回答