wait()、 notify()、 notifyAll()相关的问题
wait()、 notify()、 notifyAll()特点与性质
- 使用必须先拥有monitor
- notify只能唤醒任何一个
- 都属于object类
- 释放锁只会释放当前锁
使用wait和notify实现生产者与消费者
/**
* 〈用wait/notify实现生产者和消费者〉
*
* @author Chkl
* @create 2020/2/29
* @since 1.0.0
*/
public class ProducerCustomerModel {
public static void main(String[] args) {
EventStorage storage = new EventStorage();
Producer producer = new Producer(storage);
Consumer consumer = new Consumer(storage);
new Thread(producer).start();
new Thread(consumer).start();
}
}
//生产者
class Producer implements Runnable {
private EventStorage storage;
public Producer(EventStorage storage) {
this.storage = storage;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
storage.put();
}
}
}
//消费者
class Consumer implements Runnable {
private EventStorage storage;
public Consumer(EventStorage storage) {
this.storage = storage;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
storage.take();
}
}
}
//阻塞队列
class EventStorage {
// 最大值
private int maxSize;
// 数据存储队列
private LinkedList<Date> storage;
public EventStorage() {
maxSize = 10;
storage = new LinkedList<>();
}
// 添加方法
public synchronized void put() {
// 当队列满了就调用wait方法释放锁,等待消费后唤醒
while (storage.size() == maxSize) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 添加到队列
storage.add(new Date());
System.out.println("仓库有了" + storage.size() + "个产品");
// 添加完成后提醒消费者消费
notify();
}
public synchronized void take() {
// 当队列空了调用wait方法释放锁等待生成
if (storage.size() == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 进行消费
System.out.println("拿到了" + storage.poll() + ",现在还剩下" + storage.size());
// 消费后提醒生产者进行生产
notify();
}
}
用程序实现两个线程交替打印0-100的奇偶数
synchronized实现
/**
* 〈用程序实现两个线程交替打印0-100的奇偶数
* 本类采用synchronized〉
*
* @author Chkl
* @create 2020/2/29
* @since 1.0.0
*/
public class waitnotifyPrintEvenSYyn {
private static int count;
private static Object lock = new Object();
//建两个线程,一个只处理偶数,一个只处理奇数(位运算)
//用synchronized做通信
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
while (count < 100) {
synchronized (lock) {
if ((count & 1) == 0) {
System.out.println
(Thread.currentThread().getName()
+ ":" + count++);
}
}
}
}
}, "偶数").start();
new Thread(new Runnable() {
@Override
public void run() {
while (count < 100) {
synchronized (lock) {
if ((count & 1) != 0) {
System.out.println
(Thread.currentThread().getName()
+ ":" + count++);
}
}
}
}
}, "奇数").start();
}
}
wait和notify优化实现
/**
* 〈连个线程交替打印0-100的两个奇偶数〉
* 用wait和notify实现
*
* @author Chkl
* @create 2020/2/29
* @since 1.0.0
*/
public class WaitNotifyprintEvenWait {
//拿到锁,就打印
//打印完,唤醒其他线程,就休眠
private static int count;
private static Object lock = new Object();
static class TurningRunning implements Runnable {
@Override
public void run() {
while (count <= 100) {
synchronized (lock) {
System.out.println
(Thread.currentThread().getName() + ":" + count++);
lock.notify();
if (count <= 100) {
try {
//如果任务未结束,让出当前的锁
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public static void main(String[] args) {
new Thread(new TurningRunning(), "偶数").start();
try {
//休眠100毫秒,保证偶数先行
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new TurningRunning(), "奇数").start();
}
}
为什么wait()需要在同步代码块内使用,而sleep()不用
- 为了让通信变得可靠,防止死锁或者永久等待的发生
如果不把wait和notify方法都放在同步块里,可能在执行wait之前,线程突然切出去了,到一个将要执行notify的线程,把notify的都执行完了之后再切回将执行wait的线程执行完wait之后,不再有线程唤醒它,造成永久等待
- sleep主要针对单个线程的,和其他线程关系都不大,所以不需要在同步代码块中执行
为什么线程通信的方法wait(),notify(),notifyAll()被定义在Object类里?而sleep()定义在Thread类里?
-
wait(),notify(),notifyAll()是锁级别的操作,而锁是属于某一个对象的。每一个对象的对象头中都有几个字节是存放锁的状态的,所以锁是绑定在对象中,而不是线程中。如果定义在Thread中,如果每一个线程持有多把锁,就不能灵活地使用了。
-
sleep()是针对于单个线程的操作,所以在Thread类中
wait方法属于Object类,那么调用Thread.wait()会怎样呢?
- 可以当成一把锁去进行wait和notify的操作
- 当线程退出的时候会自动的执行notify,会干扰设计的整个流程
如何选择notify()和notifyAll()
根据业务需求选择
- notify()随机的唤醒一个线程
- notifyAll()唤醒所有的wait状态的线程
notifyAll之后所有的线程会再次抢夺锁,如果线程抢夺失败怎么办?
陷入等待状态,等待这把锁被释放后再次竞争
用suspend和resume来阻塞线程可以吗?
由于安全问题,这两个方法都已弃用了。