合作机构:阿里云 / 腾讯云 / 亚马逊云 / DreamHost / NameSilo / INWX / GODADDY / 百度统计
我们都知道在 Java 中为了保证一些操作的安全性,就会涉及到使用锁,但是你对 Java 的锁了解的有多少呢?Java 都有哪些锁?以及他们是怎么实现的,今天了不起就来说说关于 Java 的锁。
乐观锁(Optimistic Locking)是一种在数据读取时不会阻塞其他读取或写入操作的锁策略,但在更新时会检查在此期间是否有其他操作修改了数据。如果数据已被修改,则更新操作会失败,通常是通过重试或抛出异常来处理。
在 Java 中,乐观锁通常是通过版本号、时间戳或其他状态信息来实现的。以下是乐观锁在 Java 中的一些常见实现方式:
版本号机制:
时间戳机制:
CAS (Compare-and-Swap) 操作:
JPA 和 Hibernate 的乐观锁:
悲观锁(Pessimistic Locking)是一种在数据处理过程中,总是假设最坏的情况来避免数据并发问题的锁策略。在Java中,悲观锁通常在数据被访问时就立即加锁,以保证在此期间其他任何事务都不能修改这个数据,直到该事务完成为止。
Java中实现悲观锁的常见方式有以下几种:
数据库行级锁和表级锁:
Java中的synchronized关键字:
ReentrantLock类:
读写锁(ReadWriteLock):
分布式锁:
在使用悲观锁时,需要注意死锁和性能问题。死锁是指两个或多个线程无限期地等待对方释放资源的情况。性能问题则可能由于锁的粒度过大(如表级锁)导致并发性能下降。
悲观锁:假设最坏的情况,每次访问数据时都会锁定数据,防止其他事务修改。
乐观锁:假设最好的情况,允许其他事务并发访问数据,但在更新时会检查数据是否被修改。
选择哪种锁策略取决于应用的具体需求和并发场景。使用乐观锁时,需要注意处理更新失败的情况,通常是通过重试、抛出异常或给用户反馈来实现的。
Java中的递归锁(ReentrantLock)是java.util.concurrent.locks包下提供的一种可重入的互斥锁,它是悲观锁的一种实现。递归锁允许一个线程多次获取同一个锁,而不会造成死锁,这对于某些需要递归调用或者在一个线程中多次需要获取同一个锁的场景非常有用。
递归锁的几个特性:
可重入性:如果一个线程已经拥有了一个递归锁,那么它可以再次获取该锁而不会阻塞。每次获取锁,都会增加锁的持有计数;每次释放锁,都会减少持有计数。只有当持有计数减少到0时,其他线程才能获取该锁。
公平性:递归锁可以是公平的也可以是非公平的。公平性意味着锁的获取是按照线程请求锁的顺序来的,而非公平性则不保证顺序。公平的递归锁可以减少“线程饥饿”的问题,但可能会降低性能。
既然我们说她是一个悲观锁的实现,那么是不是可以和 synchronized 比较一下,有什么不同呢?
与Java内置的synchronized关键字相比,递归锁提供了更高的灵活性和更好的性能控制。例如,递归锁支持尝试获取锁(tryLock()方法)、定时获取锁(tryLock(long timeout, TimeUnit unit)方法)以及中断等待锁的线程(lockInterruptibly()方法)。
我们看一下递归锁的示例代码:
import java.util.concurrent.locks.ReentrantLock;
public class RecursiveLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void someMethod() {
lock.lock();
try {
// 临界区代码
// ...
someNestedMethod();
// ...
} finally {
lock.unlock();
}
}
private void someNestedMethod() {
lock.lock();
try {
// 嵌套调用中需要同步的代码
// ...
} finally {
lock.unlock();
}
}
}
TOP