Java中数据库常用的两把锁是乐观锁和悲观锁。
什么是乐观锁和悲观锁?
悲观锁
悲观锁假定在执行操作时会产生并发冲突,因此在操作数据前先加锁,确保操作数据时不会被其他人修改。悲观锁的典型实现就是数据库中的行锁、表锁。
在Java中,悲观锁常用的实现就是synchronized关键字和ReentrantLock类。
乐观锁
乐观锁假定在执行操作时不会产生并发冲突。因此,在对数据进行操作之前并不会先去加锁,而是在提交更新时,比对刚才读取出来的数据和当前数据库中最新的数据看是否一致,如果一致就更新数据,否则认为是冲突,需要进行回滚或者其他的错误处理。
乐观锁常用的实现就是使用版本号或时间戳机制。
乐观锁和悲观锁的区别
乐观锁和悲观锁的主要区别在于对资源的锁定方式。
在悲观锁中,在访问数据前先会对数据进行加锁,确保数据不会被其他人意外修改,这种方式保证了并发的可靠性,但是并发度不高。
在乐观锁中,不会对数据进行加锁,而是在对数据进行更新时,比较读取的数据版本和当前数据库中最新数据版本是否一致,如果一致,则说明没有其他并发更新请求。这种方式保证了并发的高效性,但是并发操作的安全性需要保证。
乐观锁和悲观锁的使用场景
悲观锁
在以下场景中,我们通常会选择使用悲观锁:
- 当一个操作耗时较长,需要对资源进行大面积修改;
- 当某个资源被频繁地访问且访问是以写操作为主;
- 当实现比较简单,不会存在死锁和数据一致性问题。
示例一:使用synchronized实现悲观锁
public synchronized void updateData(Object data) {
// 确保操作前资源被加锁
// 更新数据...
}
乐观锁
在以下场景中,我们通常会选择使用乐观锁:
- 当并发访问量比较大,使用悲观锁会导致性能瓶颈;
- 当读操作的次数远大于写操作的次数;
- 当需要保证不会出现死锁和卡顿时。
示例二:使用版本号实现乐观锁
public void updateData(Object data) {
// 查询数据版本
int version = queryVersion(data.id);
// 更新数据(更新数据时判断版本是否一致)
if (version == data.version) {
// 更新数据...
// 版本号+1
updateVersion(data.id, version+1);
} else {
// 数据版本不一致,抛出异常或进行错误处理
throw new OptimisticLockException("版本号不一致");
}
}
以上就是本文对乐观锁和悲观锁的详细讲解,以及乐观锁和悲观锁的使用场景、示例的介绍。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中数据库常用的两把锁之乐观锁和悲观锁 - Python技术站