在 Java 中,同步(Synchronization)和锁(Locks)都是用于管理多线程之间对共享资源的访问,以避免并发问题,如数据不一致或者线程干扰等问题。虽然它们的目的相同,但具体的实现机制和使用场景有所不同。
同步(Synchronization)
同步是 Java 中原生支持的线程同步机制,主要是通过 synchronized
关键字来实现的。你可以将方法或代码块标记为 synchronized
,这样一来,该方法或代码块就只能由一个线程同时执行。
- 同步方法:如果你声明一个方法为
synchronized
,那么它获取的是调用该方法的对象的锁。 - 同步代码块:你也可以同步一个特定的代码块而不是整个方法。这种情况下,你需要指定一个锁对象。
public class Counter {
private int count = 0;
// 同步方法
public synchronized void increment() {
count++;
}
// 同步代码块
public void decrement() {
synchronized (this) { // 使用当前实例作为锁
count--;
}
}
}
锁(Locks)
Java 并发 API 提供了 java.util.concurrent.locks
包,里面含有更加灵活的锁机制。这些锁需要显示地创建、锁定和释放。Lock
接口提供了比 synchronized
关键字更丰富的锁操作。它允许尝试非阻塞地获取锁(tryLock),尝试获取锁直到中断,以及尝试获取锁直到超时。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 释放锁
}
}
public void decrement() {
lock.lock();
try {
count--;
} finally {
lock.unlock();
}
}
}
区别和何时使用
- 灵活性:Lock 提供了更高的灵活性。你可以尝试获取锁,获取锁期间可以被中断,以及尝试获取锁直到超时。
- 控制精度:
synchronized
关键字自动管理锁的获取与释放。而 Lock 则需要你手动获取和释放,这提供了更精细的控制,但也增加了复杂度。 - 条件变量支持:Lock 还支持条件变量 (
Condition
),这允许线程在特定条件为真之前挂起,并且可以在条件可能为真时被唤醒。
总的来说,如果你需要更简单的线程同步, synchronized
关键字通常是足够的。而对于更复杂的需求(如尝试非阻塞获取锁、可中断的锁获取等),使用 Lock 可能是更好的选择。