- 原始结构
- synchronized 是关键字属于 JVM 层面,反应在字节码上是 monitorenter 和 monitorexit,其底层是通过 monitor 对象来完成,wait/notify 等方法也是依赖 monitor 对象,只有在同步块或方法中才能调用 wait/notify 等方法
- Lock 是具体类(java.util.concurrent.locks.Lock),是 api 层面的锁
- 使用方法
- synchronized 不需要用户手动去释放锁,当 synchronized 代码执行完后系统会自动让线程释放对锁的占用
- ReentrantLock 则需要用户手动的释放锁,若没有主动释放锁,可能导致出现死锁的现象,lock() 和 unlock() 方法需要配合 try/finally 语句来完成
- 等待是否可中断
- synchronized 不可中断,除非抛出异常或者正常运行完成。
- ReentrantLock 可中断,设置超时方法 tryLock(long timeout, TimeUnit unit) 或者代码中调用 lockInterruptibly 方法中断
- 加锁是否公平
- synchronized 非公平锁
- ReentrantLock 默认非公平锁,构造方法中可以传入 boolean 值,true 为公平锁,false 为非公平锁
- 精确唤醒
- synchronized 没有 Condition,要么随机唤醒一个线程要么唤醒全部线程
- ReentrantLock 用来实现分组唤醒需要唤醒的线程们,可以精确唤醒
/**
* @author LiuHuan
* @date 2020-06-17 15:52
* @desc Lock精确唤醒:A线程打印5次之后唤醒B线程打印10次之后唤醒C线程打印15次,之后再唤醒A,重复3轮
*/
public class ConditionTest {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(() -> {
for (int i = 0; i < 3; i++) {
shareResource.print5();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 3; i++) {
shareResource.print10();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 3; i++) {
shareResource.print15();
}
}, "C").start();
}
}
class ShareResource {
// A 1 B 2 c 3
private int number = 1;
// 创建一个重入锁
private Lock lock = new ReentrantLock();
// 这三个相当于备用钥匙
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void print5() {
lock.lock();
try {
// 判断
while(number != 1) {
condition1.await();
}
// 干活
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " " + number + " " + i);
}
// 唤醒 (干完活后,需要通知B线程执行)
number = 2;
// 通知2号去干活了
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10() {
lock.lock();
try {
// 判断
while(number != 2) {
// 不等于1,需要等待
condition2.await();
}
// 干活
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + number + " " + i);
}
// 唤醒 (干完活后,需要通知C线程执行)
number = 3;
// 通知2号去干活了
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15() {
lock.lock();
try {
// 判断
while(number != 3) {
// 不等于1,需要等待
condition3.await();
}
// 干活
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() + " " + number + " " + i);
}
// 唤醒 (干完活后,需要通知C线程执行)
number = 1;
// 通知1号去干活了
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
输出:
A 1 0
A 1 1
A 1 2
A 1 3
A 1 4
B 2 0
B 2 1
B 2 2
B 2 3
B 2 4
B 2 5
B 2 6
B 2 7
B 2 8
B 2 9
C 3 0
C 3 1
C 3 2
C 3 3
C 3 4
C 3 5
C 3 6
C 3 7
C 3 8
C 3 9
C 3 10
C 3 11
C 3 12
C 3 13
C 3 14
A 1 0
A 1 1
A 1 2
A 1 3
A 1 4
B 2 0
B 2 1
B 2 2
B 2 3
B 2 4
B 2 5
B 2 6
B 2 7
B 2 8
B 2 9
C 3 0
C 3 1
C 3 2
C 3 3
C 3 4
C 3 5
C 3 6
C 3 7
C 3 8
C 3 9
C 3 10
C 3 11
C 3 12
C 3 13
C 3 14
A 1 0
A 1 1
A 1 2
A 1 3
A 1 4
B 2 0
B 2 1
B 2 2
B 2 3
B 2 4
B 2 5
B 2 6
B 2 7
B 2 8
B 2 9
C 3 0
C 3 1
C 3 2
C 3 3
C 3 4
C 3 5
C 3 6
C 3 7
C 3 8
C 3 9
C 3 10
C 3 11
C 3 12
C 3 13
C 3 14