概述
1.wait、notify介绍,与锁的关系;
2.wait、notify、notifyAll的使用;
3.生产者消费者通过wait、notify来实现
wait、notify介绍,与锁的关系
1.wait、notify、notifyAll不属于Thread类,而是属于object类,也就是说每个对象都有这几种方法。那为什么要放到object类? 因为每个对象都有锁,而wait、notify、notifyAll是基于对象锁的。
wait会使当前线程进入等待状态,直到其他线程调用此对象的notify或notifyAll方法,或被其他线程中断。调用wait的线程必须持有对象锁。
notify 唤醒在此对象监视器上的单个线程,如果有多个线程则随机唤醒一个。直到当前线程释放锁,才能执行被唤醒的线程。notify也只能被持有对象锁的线程调用。
notifyAll也是一样,不同的是notifyAll会唤醒此对象监视器上的所有线程。
只能被持有对象锁的线程调用意味着wait/notify/notifyAll必须和synchronized方法/代码块中使用,而synchronized代码块内必须要有锁。
可以试一下,在一个非synchronized代码块中调用wait/notify会出现什么问题?
以下两个例子分别为在非synchronized代码块中调用和在synchronized代码块中调用但是未持有对象锁
public class WaitNotify { public static void main(String[] args) throws InterruptedException { Object obj = new Object(); obj.wait(); obj.notifyAll(); } } 报错: Exception in thread "main" java.lang.IllegalMonitorStateException
public class WaitNotify { public static void main(String[] args) throws InterruptedException { Object obj = new Object(); Object lock = new Object(); synchronized (lock) { obj.wait(); obj.notifyAll(); } } } 报错: Exception in thread "main" java.lang.IllegalMonitorStateException
为什么必须获取该对象的锁?
因为在没有锁的情况下,wait和notify会出现竞态,举个常用的生产者消费者的例子
假设有如下情景:
1.生产者生成->2.生成队列满了->3.生产者进入等待状态
a.消费者消费一个->b.生产者队列刷新->c.唤醒生产者线程
在多线程的情况下,可能会出现这样的顺序:1->a->b->c->3,这样无对象锁会造成的问题就是生产者还没进入等待状态,消费者就已经notify了,这样生产者会一直处在wait状态;
所有必须通过对象锁来实现,即队列对象的锁。来保证同步
wait、notify、notifyAll的使用
直接上代码了,在注释里面说明
public class RunnableTest implements Runnable { private Integer ticket; public RunnableTest(int ticket){ this.ticket=ticket; } @Override public void run() { synchronized (ticket){ //对ticket对象进行synchronized try { System.out.println(Thread.currentThread().getName()+" wait..."); ticket.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for(int i=0;i<10;i++){ if(this.ticket>0){ System.out.println(Thread.currentThread().getName()+" sell "+this.ticket--); } } } public static void main(String[] args){ RunnableTest runnableTest=new RunnableTest(5); Thread test1=new Thread(runnableTest); Thread test2=new Thread(runnableTest); Thread test3=new Thread(runnableTest); test1.start(); test2.start(); System.out.println("after test2"); test3.start(); //到这里,三个线程会进入等待状态 try { Thread.sleep(1000); //这时主线程等待1s,主要担心三个线程可能有某个线程还未wait就调用了notify了,这样线程会一直等待。 } catch (InterruptedException e) { e.printStackTrace(); } synchronized (runnableTest.ticket){ runnableTest.ticket.notify(); //唤醒线程,这时输出结果还有两个线程处于等待状态, 如果使用notifyAll就不会。 } } }
生产者消费者通过wait、notify来实现
//生产者消费者类 public class ProduceConsumerWaitNotify { public static void main(String[] args){ QueueClass resource = new QueueClass(); //生产者线程 Producer p = new Producer(resource); //多个消费者 Consumer c1 = new Consumer(resource); Consumer c2 = new Consumer(resource); Consumer c3 = new Consumer(resource); p.start(); c1.start(); c2.start(); c3.start(); } } class Producer extends Thread{ QueueClass queueClass; public Producer(QueueClass queueClass){ this.queueClass=queueClass; } @Override public void run() { while(true){ queueClass.add(); try { Thread.sleep((long)(1000*Math.random())); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Consumer extends Thread{ QueueClass queueClass; public Consumer(QueueClass queueClass){ this.queueClass=queueClass; } @Override public void run() { while(true){ try { Thread.sleep((long) (1000 * Math.random())); } catch (InterruptedException e) { e.printStackTrace(); } queueClass.remove(); } } }
//队列实现,通过wait,notifyAll来实现 public class QueueClass { private int size=10; private int count=0; public synchronized void add(){ if(count<size){ count++; System.out.println(Thread.currentThread().getName()+" produce,now is "+count); notifyAll(); //唤醒在QueueClass对象锁上的等待线程 }else{ try { wait(); //生成队列满了,持有当前对象锁的线程进入等待 System.out.println(Thread.currentThread().getName()+" producer waiting..."); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void remove(){ if(count>0){ count--; System.out.println(Thread.currentThread().getName()+" consumer,now is "+count); notifyAll(); }else{ try { wait(); System.out.println(Thread.currentThread().getName()+" consumer waiting..."); } catch (InterruptedException e) { e.printStackTrace(); } } } }