在Java并发编程的世界中,线程安全是一个至关重要的概念。为了确保数据的一致性和完整性,我们需要使用同步机制来控制多个线程对共享资源的访问。Java提供了多种同步机制,其中最常用的是synchronized关键字和ReentrantLock类。本文将对这两种机制进行详细分析,并通过实例展示它们的使用方法和适用场景。
首先,我们来看一下synchronized关键字。synchronized是Java内置的同步机制,它可以用于修饰方法或代码块。当一个线程进入一个被synchronized修饰的方法或代码块时,它会获取到对象的锁,其他线程需要等待该锁释放后才能进入。这种机制简单易用,适用于大部分并发场景。
然而,synchronized也有一些局限性。例如,它无法尝试获取锁,也无法被中断,这可能导致死锁问题。此外,它的粒度较粗,无法提供更细粒度的锁定控制。为了解决这些问题,Java提供了ReentrantLock类。
ReentrantLock是java.util.concurrent.locks包中的一个类,它提供了比synchronized更丰富的功能。首先,ReentrantLock支持尝试获取锁,如果获取不到锁,线程可以立即返回,而不会一直等待。其次,ReentrantLock可以被中断,这意味着线程在等待锁的过程中可以被其他线程打断。最后,ReentrantLock还提供了公平锁和非公平锁两种模式,可以根据实际需求选择合适的模式。
下面,我们通过一个例子来展示synchronized和ReentrantLock的使用。假设我们有一个银行账户类,需要实现转账操作。为了确保数据一致性,我们需要在转账过程中对账户余额进行同步控制。
首先,我们使用synchronized实现转账操作:
class BankAccount {
private double balance;
public synchronized void transfer(BankAccount target, double amount) {
if (balance >= amount) {
balance -= amount;
target.balance += amount;
}
}
}
然后,我们使用ReentrantLock实现相同的功能:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class BankAccountWithLock {
private double balance;
private final Lock lock = new ReentrantLock();
public void transfer(BankAccountWithLock target, double amount) {
lock.lock();
try {
if (balance >= amount) {
balance -= amount;
target.balance += amount;
}
} finally {
lock.unlock();
}
}
}
通过对比,我们可以看到ReentrantLock提供了更多的灵活性和控制力。然而,这也带来了更高的复杂性。在实际开发中,我们应根据具体需求和场景选择合适的同步机制。