介绍
Java Persistence API带有完善的并发控制机制,支持隐式和显式锁定。 隐式锁定机制很简单,它依赖于:
虽然隐式锁定适用于许多情况,但显式锁定机制可以利用更细粒度的并发控制。
在之前的文章中,我介绍了显式的乐观锁定模式:
在这篇文章中,我将解开显式的悲观锁模式:
读写器锁
数据库系统是高度并发的环境,因此许多并发理论习惯用法也适用于数据库访问。 必须将并发更改序列化以保留数据完整性,因此,即使通常通过Multiversion并发控制机制对其进行补充,大多数数据库系统也使用两阶段锁定策略。
因为互斥锁定会阻碍可伸缩性(平等地进行读写操作),所以大多数数据库系统都使用读写器锁定同步方案,因此:
- 共享(读取)锁会阻止作者,从而允许多个读者继续
- 排他(写入)锁同时阻止读取器和写入器,从而使所有写入操作顺序地应用
因为锁定语法不是SQL标准的一部分,所以每个RDBMS都选择了不同的语法:
数据库名称 | 共享锁语句 | 排他锁声明 |
---|---|---|
Oracle | 更新 | 更新 |
MySQL | 锁定共享模式 | 更新 |
Microsoft SQL服务器 | 带(HOLDLOCK,ROWLOCK) | 带(上锁,上锁) |
PostgreSQL | 分享 | 更新 |
DB2 | 只供RS阅读 | 用于RS更新 |
Java持久性抽象层隐藏了数据库特定的锁定语义,提供了仅需要两个锁定模式的通用API。 使用PESSIMISTIC_READ锁定模式类型获取共享/读取锁定,而使用PESSIMISTIC_WRITE请求排他/写入锁定。
PostgreSQL行级锁定模式
在下一个测试案例中,我们将使用PostgreSQL,因为它既支持独占锁定 ,也支持共享显式锁定 。
以下所有测试将使用相同的并发实用程序,模拟两个用户:Alice和Bob。 每个测试方案将验证特定的读/写锁定组合。
private void testPessimisticLocking(ProductLockRequestCallable primaryLockRequestCallable, ProductLockRequestCallable secondaryLockRequestCallable) {
doInTransaction(session -> {
try {
Product product = (Product) session.get(Product.class, 1L);
primaryLockRequestCallable.lock(session, product);
executeAsync(
() -> {
doI