wait()、notify()、notifyAll() wait()、notify()、notifyAll()这三个函数都是Object类中的方法,而Object类是所有类的父类,所以所有对象实例都有该方法.
wait():阻塞当前之前直到该对象(调用wait函数的对象)在另一个线程调用了notify()或者notifyAll(); notify():唤醒单个线程 notifyAll():唤醒所有线程
这三个方法,都是Java语言提供的实现线程间阻塞(Blocking)和控制进程内调度(inter-process communication)的底层机制。在解释如何使用前,先说明一下两点:
正如Java内任何对象都能成为锁(Lock)一样,任何对象也都能成为条件队列(Condition queue)。而这个对象里的wait(), notify()和notifyAll()则是这个条件队列的固有(intrinsic)的方法。
一个对象的固有锁和它的固有条件队列是相关的,为了调用对象X内条件队列的方法,你必须获得对象X的锁。这是因为等待状态条件的机制和保证状态连续性的机制是紧密的结合在一起的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class TestWait { public static void main(String[]args) { TestWaitBean bean = new TestWaitBean("bean"); System.out.println("init bean"); System.out.println("invoke bean wait"); try { bean.wait(); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("wait finished"); } } static class TestWaitBean { String name; public TestWaitBean(String name) { this.name = name; } } }
上述代码运行后会抛出异常java.lang.IllegalMonitorStateException 因为未获取对象bean的锁,就去调用bean.wait()
修改一下代码,实现一个简单的阻塞、唤醒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class TestWait { public static void main(String[]args) { TestWaitBean bean = new TestWaitBean("bean"); System.out.println("init bean"); System.out.println("invoke bean wait"); WeakUpThread thread = new WeakUpThread(bean); thread.start(); synchronized (bean) { try { bean.wait(); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("wait finished"); } } } static class TestWaitBean { String name; public TestWaitBean(String name) { this.name = name; } } static class WeakUpThread extends Thread { Object lock; public WeakUpThread(Object lock) { this.lock = lock; } @Override public void run() { super.run(); synchronized (lock) { try { System.out.println("Current Thread is sleep 2000ms" ); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println(" weakUp!!!"); lock.notify(); } } } } }
注意:
不管是调用wait,还是notify、notifyAll,都要是在同步修饰的代码块或者方法中,即必须先获取对象锁,在调用对象方法
使用wait、notifyAll实现一个多线程的生产者、消费者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 class BlockTest { public static void main(String []args) { List<Object>list = new ArrayList<>(); Block block = new Block(list); list.add(null); Thread thread1 = new Thread(new PutThread(block, new Person("person1")), "thread1"); Thread thread2 = new Thread(new PutThread(block, new Person("person2")), "thread2"); Thread thread3 = new Thread(new PutThread(block, new Person("person3")), "thread3"); Thread thread4 = new Thread(new PutThread(block, new Person("person4")), "thread4"); Thread thread5 = new Thread(new OutThread(block), "thread5"); Thread thread6 = new Thread(new OutThread(block), "thread6"); Thread thread7 = new Thread(new OutThread(block), "thread7"); thread1.start(); thread2.start(); thread5.start(); thread6.start(); thread7.start(); thread3.start(); thread4.start(); } static class PutThread implements Runnable { Block block; Person person; public PutThread(Block block, Person person) { this.block = block; this.person = person; } @Override public void run() { System.out.println(Thread.currentThread().getName() + " puting the person name is " + person.getName()); block.put(person); } } static class OutThread implements Runnable { Block block; public OutThread(Block block) { this.block = block; } @Override public void run() { Person person = (Person) block.get(); if(person != null) { System.out.println(Thread.currentThread().getName() + " get the person name is " + person.getName()); } else { System.out.println(Thread.currentThread().getName() + " the person is null"); } } } static class Block<T> { List<T>t; Object lock = new Object(); int currentIndex = 0; volatile boolean isRead = false; public Block(List<T>t){ this.t= t; } public void put(T at) { if(t == null) throw new NullPointerException("t is null"); synchronized (lock) { try { if(isRead) { lock.wait(); } currentIndex++; t.add(at); lock.notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } } } public T get() { synchronized (lock){ T at = null; try { if(currentIndex == 0) { System.out.println(Thread.currentThread().getName() + " 当前下标已为0 阻塞 等待写入再取"); lock.wait(); } if(isRead){ System.out.println(Thread.currentThread().getName() + " 当前正在读 阻塞 等待写入再取 index" + currentIndex); lock.wait(); } isRead = true; at = t.remove(currentIndex); if(at == null) { System.out.println(Thread.currentThread().getName() + " index" + currentIndex); return null; } currentIndex --; return at; } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.notifyAll(); isRead = false; return at; } } } } static class Person{ private String name = ""; public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } }
上述生产者、消费者只能适用于多个线程直接的put、get 因为wait、和notify/notifyAll本身就是互斥的,因为调用wait后,就会阻塞当前调用线程, 本身线程的notify/notifyAll也就不会被调用,所以说上述代码只能实现多个线程直接的场景, 还有就是上述代码只是简单的实现,而且是读操作加锁,一般而言应该是写操作时堵塞。