在 Java 并发编程中,synchronized
和 ReentrantLock
都是非常重要的同步机制,用于控制多个线程对共享资源的访问。虽然它们都能实现线程同步,但在实际使用中,两者之间存在一些重要的区别。本文将以教程的形式详细介绍 synchronized
和 ReentrantLock
的区别,并通过示例代码展示它们的使用方法。
synchronized 关键字
synchronized
是 Java 中的一个关键字,它可以用于方法或者代码块,用来声明一个同步区域。当一个线程进入 synchronized
修饰的代码块时,它会自动获取锁;当它退出该代码块时,锁也会自动释放。synchronized
锁是 JVM 层面实现的,它具有以下特点:
- 自动获取与释放锁:不需要显式地获取和释放锁,简化了代码编写。
- 不可中断:当一个线程持有锁时,其他线程只能等待,即使发生中断也无法打断。
- 支持重入:同一个线程可以多次获取同一把锁,每次获取锁都必须匹配一次释放。
- 锁粒度:可以作用于方法级别或代码块级别。
- 非公平锁:默认情况下,
synchronized
锁是非公平的,这意味着它不能保证线程的公平性。
示例代码
以下是一个使用 synchronized
的示例代码,用于实现一个简单的计数器类:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
public class SynchronizedCounterDemo {
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + counter.getCount());
}
}
ReentrantLock 类
ReentrantLock
是 java.util.concurrent.locks
包中的一个接口实现,它提供了比 synchronized
更加灵活的锁定机制。ReentrantLock
具有以下特点:
- 显式获取与释放锁:使用
lock()
和unlock()
方法显式地获取和释放锁。 - 可中断:线程在等待锁的过程中可以被中断。
- 支持公平锁:可以设置为公平锁或非公平锁,默认是非公平锁。
- 支持条件变量:
ReentrantLock
提供了条件变量的支持,可以通过newCondition()
方法创建条件对象。 - 更细粒度的控制:提供了更多的控制选项,如尝试获取锁、可重入性等。
示例代码
以下是一个使用 ReentrantLock
的示例代码,用于实现一个简单的计数器类:
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
public class ReentrantLockCounterDemo {
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + counter.getCount());
}
}
总结
通过上述教程,我们可以了解到 synchronized
和 ReentrantLock
在 Java 并发编程中的不同之处。虽然两者都能实现线程同步,但在实际使用中,ReentrantLock
提供了更多的灵活性和控制选项,而 synchronized
更加简单易用。选择哪种同步机制取决于具体的应用场景和需求。无论是在日常开发还是面试准备中,熟悉这两种同步机制的区别都是非常重要的。