【悲观锁】Synchronized和ReentrantLock的区别
【线程方法】多线程下 wait、notify、park、unpark 和 await、signal 的区别
【同步工具】CyclicBarrier和CountDownLatch的区别
1、锁的获取、使用方式
synchronized可以作用于对象、方法、代码块、类上,ReentrantLock则作用于lock对象上。在使用 ReentrantLock
时,一定要在 finally
块中释放锁,以确保锁的释放不受异常的影响。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SynchronizedVsReentrantLockExample {
private static int counter = 0;
// 使用synchronized关键字进行线程同步
public synchronized static void synchronizedMethod() {
for (int i = 0; i < 5; i++) {
counter++;
System.out.println(Thread.currentThread().getName() + " - Synchronized: " + counter);
}
}
// 使用ReentrantLock进行线程同步
public static void reentrantLockMethod() {
Lock lock = new ReentrantLock();
lock.lock(); // 获取锁
try {
for (int i = 0; i < 5; i++) {
counter++;
System.out.println(Thread.currentThread().getName() + " - ReentrantLock: " + counter);
}
} finally {
lock.unlock(); // 释放锁
}
}
public static void main(String[] args) {
// 使用synchronized关键字的线程
Thread thread1 = new Thread(() -> synchronizedMethod());
Thread thread2 = new Thread(() -> synchronizedMethod());
// 使用ReentrantLock的线程
Thread thread3 = new Thread(() -> reentrantLockMethod());
Thread thread4 = new Thread(() -> reentrantLockMethod());
// 启动线程
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
2、公平性区别
在 ReentrantLock
中,可以选择使用公平锁或非公平锁,默认为非公平锁。在 synchronized
中,默认是非公平锁。当使用公平锁时,线程按照请求锁的顺序获得锁,而非公平锁为堵塞队列中的线程一起抢夺,可能导致某些线程长时间无法获得锁。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class FairnessExample {
private static int counter = 0;
// 使用synchronized关键字进行线程同步(非公平锁)
public synchronized static void synchronizedMethod() {
for (int i = 0; i < 5; i++) {
counter++;
System.out.println(Thread.currentThread().getName() + " - Synchronized: " + counter);
}
}
// 使用ReentrantLock进行线程同步(默认为非公平锁)
public static void reentrantLockMethod() {
Lock lock = new ReentrantLock(); // 默认为非公平锁
lock.lock(); // 获取锁
try {
for (int i = 0; i < 5; i++) {
counter++;
System.out.println(Thread.currentThread().getName() + " - ReentrantLock: " + counter);
}
} finally {
lock.unlock(); // 释放锁
}
}
// 使用ReentrantLock进行线程同步(公平锁)
public static void fairReentrantLockMethod() {
Lock fairLock = new ReentrantLock(true); // 创建公平锁
fairLock.lock(); // 获取锁
try {
for (int i = 0; i < 5; i++) {
counter++;
System.out.println(Thread.currentThread().getName() + " - Fair ReentrantLock: " + counter);
}
} finally {
fairLock.unlock(); // 释放锁
}
}
public static void main(String[] args) {
// 使用synchronized关键字的线程
Thread thread1 = new Thread(() -> synchronizedMethod());
Thread thread2 = new Thread(() -> synchronizedMethod());
// 使用ReentrantLock的线程(非公平锁)
Thread thread3 = new Thread(() -> reentrantLockMethod());
Thread thread4 = new Thread(() -> reentrantLockMethod());
// 使用ReentrantLock的线程(公平锁)
Thread thread5 = new Thread(() -> fairReentrantLockMethod());
Thread thread6 = new Thread(() -> fairReentrantLockMethod());
// 启动线程
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
thread6.start();
}
}
3、可中断性
ReentrantLock
具有可中断性,在示例中,两个线程分别尝试获取同一个锁,然后主线程在一定时间后中断一个线程,观察两者的行为。reentrantLock.lockInterruptibly()设置此线程可被打断。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class InterruptibilityExample {
private static final Object lock = new Object();
private static final Lock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
// 使用synchronized关键字的线程
Thread thread1 = new Thread(() -> {
synchronized (lock) {
try {
System.out.println(Thread.currentThread().getName() + " - Acquiring lock");
Thread.sleep(5000); // 模拟长时间持有锁
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " - Interrupted while waiting for lock");
}
}
});
// 使用ReentrantLock的线程
Thread thread2 = new Thread(() -> {
try {
reentrantLock.lockInterruptibly();
try {
System.out.println(Thread.currentThread().getName() + " - Acquiring lock");
Thread.sleep(5000); // 模拟长时间持有锁
} finally {
reentrantLock.unlock();
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " - Interrupted while waiting for lock");
}
});
// 启动线程
thread1.start();
thread2.start();
// 主线程等待一段时间
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 中断线程2
thread2.interrupt();
}
}
4、定时锁等待
ReentrantLock
提供了定时锁等待的功能,可以使用 tryLock(long timeout, TimeUnit unit)
方法实现。这个方法会尝试在指定的时间内获取锁,如果在规定时间内未获取到锁,则返回 false。
在使用 tryLock
方法时,需要注意处理 InterruptedException
,因为 tryLock
方法可能被中断(可被打断)。在中断发生时,需要适当处理中断异常。tryLock
方法可有效解决哲学家就餐死锁问题。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockTimeoutExample {
private static int counter = 0;
public static void main(String[] args) {
Lock lock = new ReentrantLock();
// 线程1
Thread thread1 = new Thread(() -> {
if (tryLockWithTimeout(lock)) {
try {
for (int i = 0; i < 5; i++) {
counter++;
System.out.println(Thread.currentThread().getName() + " - Increment: " + counter);
}
} finally {
lock.unlock();
}
} else {
System.out.println(Thread.currentThread().getName() + " - Unable to acquire lock within timeout.");
}
});
// 线程2
Thread thread2 = new Thread(() -> {
if (tryLockWithTimeout(lock)) {
try {
for (int i = 0; i < 5; i++) {
counter--;
System.out.println(Thread.currentThread().getName() + " - Decrement: " + counter);
}
} finally {
lock.unlock();
}
} else {
System.out.println(Thread.currentThread().getName() + " - Unable to acquire lock within timeout.");
}
});
// 启动线程
thread1.start();
thread2.start();
}
private static boolean tryLockWithTimeout(Lock lock) {
try {
// 尝试在2秒内获取锁
return lock.tryLock(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
}
5、多条件变量
ReentrantLock
支持创建多个条件变量,可以通过多次调用newCondition()
方法创建多个Condition
对象,以便更精细地控制线程的等待和通知。- 在
synchronized
中,一个对象只有一个相关的等待集合和通知集合,无法直接支持多个条件变量。 - await前需要获得锁、await 执行后,会释放锁,进入 conditionObject 等待 、await 的线程被唤醒(或打断、或超时)需重新竞争 lock、 锁竞争lock锁成功后,从await 后继续执行。两种唤醒
signal()
和signalAll()
。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MultiConditionExample {
private final Lock lock = new ReentrantLock();
private final Condition conditionA = lock.newCondition();
private final Condition conditionB = lock.newCondition();
private boolean conditionMetA = false;
private boolean conditionMetB = false;
public void waitForConditionA() throws InterruptedException {
lock.lock();
try {
while (!conditionMetA) {
conditionA.await();
}
// 执行条件A满足时的操作
System.out.println(Thread.currentThread().getName() + " - Condition A met");
} finally {
lock.unlock();
}
}
public void signalConditionA() {
lock.lock();
try {
// 执行满足条件A的操作
System.out.println(Thread.currentThread().getName() + " - Signaling Condition A");
conditionMetA = true;
conditionA.signalAll();
} finally {
lock.unlock();
}
}
public void waitForConditionB() throws InterruptedException {
lock.lock();
try {
while (!conditionMetB) {
conditionB.await();
}
// 执行条件B满足时的操作
System.out.println(Thread.currentThread().getName() + " - Condition B met");
} finally {
lock.unlock();
}
}
public void signalConditionB() {
lock.lock();
try {
// 执行满足条件B的操作
System.out.println(Thread.currentThread().getName() + " - Signaling Condition B");
conditionMetB = true;
conditionB.signalAll();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
MultiConditionExample example = new MultiConditionExample();
// 线程1等待条件A
Thread thread1 = new Thread(() -> {
try {
example.waitForConditionA();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 线程2等待条件B
Thread thread2 = new Thread(() -> {
try {
example.waitForConditionB();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 线程3满足条件A
Thread thread3 = new Thread(() -> example.signalConditionA());
// 线程4满足条件B
Thread thread4 = new Thread(() -> example.signalConditionB());
thread1.start();
thread2.start();
// 休眠一段时间,确保线程1和线程2已经在等待条件
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread3.start(); // 满足条件A
thread4.start(); // 满足条件B
}
}