Java—wait和notify的使用

wait和notify的使用

规则

  1. wait和notify都只能在synchronized代码块里面调用
  2. synchronized(object)的语义是只有持有object锁的对象才能进入,同一时间只能有一个线程持有该锁
  3. wait和notify的调用者最好是final修饰的对象,否则容易出现对象被改变之后的IllegalMonitorStateException错误
  4. 进入了synchronized代码块代表获取了该对象的锁
  5. 调用object.wait代表暂时释放object锁并处于阻塞状态,这时,另外的线程就可以进入synchronized代码块了
  6. 调用object.notify会唤醒之前调用了object.wait的(也就是处于阻塞状态的)线程,但是锁的释放要等到当前的同步代码块执行完毕才释放
  7. synchronized代码块里,object.notify之后的代码会继续执行,并且能够使用object对象

使用

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author mingC
 * @date 2018/7/7
 */
public class WaitAndNotify {
	final Object lock = new Object();
	CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

	class Consumer implements Runnable {

		@Override
		public void run() {
			synchronized (lock) {
				System.out.println(Thread.currentThread().getName() + "进入同步块");
				System.out.println(Thread.currentThread().getName() + "wait()");
				try {
					lock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "唤醒..");
			}
		}
	}

	class Producer implements Runnable {

		@Override
		public void run() {
			synchronized (lock) {
				System.out.println(Thread.currentThread().getName() + "进入同步块");
				System.out.println(Thread.currentThread().getName() + "notify()");
				lock.notify();
				System.out.println(lock);
				System.out.println("测试notify调用后,后面的代码是否还执行");
			}
		}
	}

	void start() {
		ExecutorService executor = Executors.newCachedThreadPool();
		executor.execute(new Consumer());
		executor.execute(new Producer());
	}

	public static void main(String[] args) {
		new WaitAndNotify().start();
	}
}

复制代码

输出

pool-1-thread-1进入同步块
pool-1-thread-1wait()
pool-1-thread-2进入同步块
pool-1-thread-2notify()
java.lang.Object@4608042a
测试notify调用后,后面的代码是否还执行
pool-1-thread-1唤醒..
复制代码

生产者消费者

合理使用wait和notify可以避免浪费cpu资源

比如生产者消费者问题,当产品数目为0时,消费者线程应当进入等待状态,避免用while(true)不断地访问,否则会浪费cpu资源。

示例 这里只考虑了单个生产者和单个消费者。

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author mingC
 * @date 2018/7/7
 */
public class WaitAndNotify {
	final Object readLock = new Object();  //读锁
	CopyOnWriteArrayList<String> productList = new CopyOnWriteArrayList<>();  //产品列表

	class Consumer implements Runnable {

		@Override
		public void run() {
			synchronized (readLock) {
				while (true) {
					//如果当前产品数为0,则不能读,进入阻塞状态
					if (productList.size() == 0) {
						try {
							System.out.println("产品数为0,消费者阻塞..");
							readLock.wait();
							System.out.println("消费者唤醒");
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					//读,这里可以保证产品数不为0
					String oldValue = productList.remove(0);
					System.out.println("消费产品:" + oldValue);
				}
			}
		}
	}

	class Producer implements Runnable {

		@Override
		public void run() {
			int count = 0;
			while (true) {
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//5秒生产一件产品,模拟读取速度远大于生产速度
				productList.add("产品" + count);
				//唤醒消费者
				if (productList.size() == 1) {
					synchronized (readLock) {
						readLock.notify();
					}
				}
				count++;
			}
		}
	}

	void start() {
		ExecutorService executor = Executors.newCachedThreadPool();
		executor.execute(new Consumer());
		executor.execute(new Producer());
	}

	public static void main(String[] args) {
		new WaitAndNotify().start();
	}
}

复制代码

输出:

产品数为0,消费者阻塞..
消费者唤醒
消费产品:产品0
产品数为0,消费者阻塞..
消费者唤醒
消费产品:产品1
产品数为0,消费者阻塞..
复制代码

转载于:https://juejin.im/post/5b40b4216fb9a04f89781403

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值