java并发编程笔记(三)--管程(三)

本文详细解析了Java中wait、notify方法的工作原理和使用场景,包括线程的BLOCKED和WAITING状态的区别,以及wait、sleep的对比。同时介绍了正确使用wait的关键点,防止虚假唤醒,并展示了保护性暂停模式的应用示例。
摘要由CSDN通过智能技术生成

1.wait notify 原理

QQ截图20220222155319

  • 持有Owner的锁对象调用wait方法(obj.wait),就会使当前线程进入WaitSet中,变为WAITING状态。

  • 处于BLOCKED和WAITING状态的线程都为阻塞状态,CPU都不会分给他们时间片。但是有所区别:

    • BLOCKED状态的线程是在竞争对象时,发现Monitor的Owner已经是别的线程了,此时就会进入EntryList中,并处于BLOCKED状态
    • WAITING状态的线程是获得了对象的锁,但是自身因为某些原因需要进入阻塞状态时,锁对象调用了wait方法而进入了WaitSet中,处于WAITING状态
  • BLOCKED状态的线程会在锁被释放的时候被唤醒,但是处于WAITING状态的线程只有被锁对象调用了notify方法(obj.notify/obj.notifyAll),才会被唤醒

  • WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入EntryList 重新竞争

只有使用了重量级锁才能调用wait方法:

public class Test1 {
	final static Object LOCK = new Object();
	public static void main(String[] args) throws InterruptedException {
        //只有在对象被锁住后才能调用wait方法
		synchronized (LOCK) {
			LOCK.wait();
		}
	}
}
API 介绍
  • obj.wait() 让进入 object 监视器的线程到 waitSet 等待

  • obj.notify()object上正在 waitSet 等待的线程中挑一个唤醒

  • obj.notifyAll()object 上正在 waitSet 等待的线程全部唤醒

Wait与Sleep的区别

不同点

  • Sleep是Thread类的静态方法,Wait是Object的方法,Object又是所有类的父类,所以所有类都有Wait方法。
  • Sleep在阻塞的时候不会释放锁,而Wait在阻塞的时候会释放锁
  • Sleep不需要与synchronized一起使用,而Wait需要与synchronized一起使用(对象被锁以后才能使用)

相同点

  • 阻塞状态都为TIMED_WAITING
正确的使用wait

说到如何正确的使用wait,不如先谈谈为什么要正确的使用wait。

当然,这与我们的老朋友sleep是分不开的。

sleep在阻塞的时候,是不释放锁的,这就导致我们很多其他要通过这个锁对象对临界区进行操作的线程一时半会无法使用这个锁对象,导致无法执行代码,影响了效率。

所以我们要使用wait代替sleep。

但是不正确的使用wait,会导致虚假唤醒的产生。

虚假唤醒:因为唤醒wait只有notifyAll和notify两个方法,没有办法指定唤醒,所以常常会出现命名没达到某几个线程的唤醒条件,但是该线程被唤醒了,这就是虚假唤醒。

我们举个例子:

  • 先说说sleep的模式

QQ截图20220222161141

QQ截图20220222161149

首先是sleep的情况,小南有烟才能干活,于是让他暂时等待,但是用sleep等待会影响后面线程的工作。

  • 用了wait

QQ截图20220222161528

QQ截图20220222161535

解决了刚才的问题,可是如果wait的线程不只一个呢?

  • 多个等待线程

QQ截图20220222161655

QQ截图20220222161713

此时小南的烟没到,却把他唤醒了,这就出现了问题,小南只有一次wait,之后就不能干活了。

我们要让他,无烟就一直等待。。

  • while+notifyAll

QQ截图20220222161907

QQ截图20220222161912

此时就涉及了一种模式,后面我们会说。

大概的模式图是这样的

synchronized (LOCK) {
	while(//不满足条件,一直等待,避免虚假唤醒) {
		LOCK.wait();
	}
	//满足条件后再运行
}

synchronized (LOCK) {
	//唤醒所有等待线程
	LOCK.notifyAll();
}

2.模式之保护性暂停

QQ截图20220222162018

public class Test2 {
	public static void main(String[] args) {
		String hello = "hello thread!";
		Guarded guarded = new Guarded();
		new Thread(()->{
			System.out.println("想要得到结果");
			synchronized (guarded) {
				System.out.println("结果是:"+guarded.getResponse());
			}
			System.out.println("得到结果");
		}).start();

		new Thread(()->{
			System.out.println("设置结果");
			synchronized (guarded) {
				guarded.setResponse(hello);
			}
		}).start();
	}
}

class Guarded {
	/**
	 * 要返回的结果
	 */
	private Object response;
	
    //优雅地使用wait/notify
	public Object getResponse() {
		//如果返回结果为空就一直等待,避免虚假唤醒
		while(response == null) {
			synchronized (this) {
				try {
					this.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		return response;
	}

	public void setResponse(Object response) {
		this.response = response;
		synchronized (this) {
			//唤醒休眠的线程
			this.notifyAll();
		}
	}

	@Override
	public String toString() {
		return "Guarded{" +
				"response=" + response +
				'}';
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值