本文为视频笔记:P50-P55
链接:尚硅谷并发编程
文章目录
1. 三种等待唤醒机制
已知Object类、ReentrantLock.newCondition、LockSupport类都有提供等待唤醒机制:
- Object类的wait、notify方法
- ReentrantLock.newCondition.await和signal方法
- LockSupport类的park和unpark方法
2. 区别
Object类和ReentrantLock.newCondition提供的阻塞唤醒机制十分相似,体现在:
- 要求阻塞和唤醒是有序的:它们都只能先阻塞再唤醒。否则会抛IllegalMonitorStateException异常,且被阻塞的线程不能被唤醒
- 依赖加锁或同步代码块:调Object的等待唤醒方法时要搭配synchronized一起用。即Object#wait和Object#notify要写在sync同步代码块内;调ReentrantLock.newCondition的await和signal方法时要搭配lock,unlock一起用。否则也会抛IllegalMonitorStateException异常,且被阻塞的线程不能被唤醒,
LockSupport类与前两者不同:
- LockSupport类支持先唤醒后阻塞,不会抛异常(底层是通过permit通行证实现的)
- LockSupport类不依赖加锁代码块,直接LockSupport.park, LockSupport.park调用即可,较方便
3. demo演示
3.1 synchronized搭配Object的wait、notify
package com.example.juc.LockCondition;
public class TestObj {
public static void main(String[] args) {
TestObj obj = new TestObj(); // 1. 待会借此生成一个sync锁
Thread t1 = new Thread(() -> {
synchronized (obj) { // 2.1 记得锁住同个对象
System.out.println("start。。。。");
try {
obj.wait(10000); // 阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end。。。。");
}
});
t1.setPriority(9);
t1.start();
new Thread(() -> {
synchronized (obj) { // 2.2 记得锁住同个对象
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("唤醒t1.....");
obj.notify(); // 通知唤醒t1
}
}).start();
}
}
3.2 ReentrantLock的await和signal方法
先看个翻车日常,使用await和signal时并没有使用同个condition调用,结果被阻塞的线程没有真正被通知唤醒:
此外,以上的try catch写得也不太规范。加锁代码应放在try之外,释放锁应该写在finally内。
正确代码演示:
package com.example.juc.LockCondition;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TestCondition {
private static ReentrantLock lock = new ReentrantLock();
private static Condition condition = lock.newCondition(); // await和signal都要使用同一个condition!
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
lock.lock(); // 代码规范1
try {
System.out.println("开始阻塞");
condition.await(10000, TimeUnit.MILLISECONDS); // 阻塞
System.out.println("结束阻塞");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 代码规范2
}
});
t1.setPriority(9);
t1.start();
new Thread(() -> {
try {
TimeUnit.MILLISECONDS.sleep(1000); // 此处设置休眠是想让t1先跑一会
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
System.out.println("唤醒t1...");
condition.signal();
lock.unlock();
}).start();
}
}
3.3 LockSupport的park和unpark
package com.example.juc.LockCondition;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
public class TestPark {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("开始阻塞");
LockSupport.park(10000); // 阻塞
System.out.println("结束阻塞");
});
t1.setPriority(9);
t1.start();
new Thread(() -> {
try {
TimeUnit.MILLISECONDS.sleep(1000); // 此处设置休眠是想让t1先跑一会
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("唤醒t1...");
LockSupport.unpark(t1); // 唤醒t1,给t1一个凭证
}).start();
}
}
4. 总结
使用这几种阻塞唤醒机制时,要注意一些细节,不要抛异常就OK