等待与唤醒与锁
About 2 min
主要介绍一下wait notify, park unpark 等的使用, 做一些例子供参考
wait与notify
wait和notify是Object类中的native方法, 且不可以重写. 在执行的时候, 依赖一个对象的监视器. 因此我们需要使用同步, 确保其能获取到对象的锁.
注意, wait之后不能没有notify, 当然也可以指定等待时间.
下面的例子是通过wait和notify实现的生产消费功能, 当生产数量达到一定值的时候唤醒消费线程生产等待, 消费到0唤醒生产线程消费等待.
public class TestWaitNotify {
private static Object obj = new Object();
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
SleepUtil.sleepSecond(1);
synchronized (obj) {
while (true) {
SleepUtil.sleepSecond(1);
if (count >= 5) {
obj.notify();
System.out.println("consumer notify");
try {
System.out.println("producer wait");
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("producer: " + ++count);
}
}
}, "生产").start();
new Thread(() -> {
synchronized (obj) {
while (true) {
if (count == 0) {
obj.notify();
System.out.println("producer notify");
try {
System.out.println("consumer wait");
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
SleepUtil.sleepSecond(1);
System.out.println("consumer used: " + --count);
}
}
}, "消费").start();
}
}
执行结果:
park和unpark
LockSupport的park和unpark是基于AQS实现的, 线程在默认情况下, 是不持有许可证的, 当park方法执行的时候会被阻塞. 执行unpark, 线程获取到许可, 因此park直接返回.
下面也是一个生产消费的例子.
public class TestParkUnpark {
private static Thread producer = new ProducerThread("生产");
private static Thread consumer = new ConsumerThread("消费");
private static volatile int count = 0;
public static void main(String[] args) {
producer.start();
consumer.start();
}
private static class ProducerThread extends Thread {
public ProducerThread(String name) {
super(name);
}
@Override
public void run() {
while (true) {
SleepUtil.sleepSecond(2);
System.out.println("producer: " + ++count);
if (count >= 5) {
LockSupport.unpark(consumer);
LockSupport.park();
}
}
}
}
private static class ConsumerThread extends Thread {
public ConsumerThread(String name) {
super(name);
}
@Override
public void run() {
while (true) {
SleepUtil.sleepSecond(1);
if (count <= 0) {
LockSupport.unpark(producer);
LockSupport.park();
}
System.out.println("consumer: " + --count);
}
}
}
}
运行结果:
比较
- unpark可以指定需要唤醒的线程, 而notify是随机唤醒, notifyAll全部唤醒, 无法唤醒指定的线程.
- wait和notify是需要对象锁的, 而park unpark是基于AQS实现的
- 两者都有可能产生死锁, 尤其是最后执行wait或者park, 因此建议有循环唤醒的机制.