首先咱们须要知道,这几个都是Object对象的方法。换言之,Java中全部的对象都有这些方法。java
public final native void notify(); public final native void notifyAll(); public final native void wait(long timeout) throws InterruptedException; public final void wait() throws InterruptedException { wait(0); }
其中notify()、notifyAll()、wait(long timeout)是本地方法
其次咱们须要知道这几个方法主要是用来个线程之间通讯的。那可能就有人会问,既然是用来线程之间通讯的,那为何这几个方法不是在线程类Thread上呢?对于这个问题,咱们先来看下这几个方法的具体使用方式再来回答这个问题。面试
Java中规定,在调用者三个方法时,当前线程必须得到对象锁。所以就得配合synchronized关键字来使用dom
//使用模式,不表明可运行代码 synchronized(object) { while(contidion) { object.wait(); } //object.notify(); //object.notifyAll(); }
或者ide
public synchronized void methodName() { while(contidion) { object.wait(); } //object.notify(); //object.notifyAll(); }
在synchronized拿到对象锁以后,synchronized代码块或者方法中,一定是会持有对象锁的,所以就可使用wait()或者notify()。
经过上述使用方法,咱们也能很好理解为何这几个方法是在Object上而不是在Thread上。由于每一个对象均可以做为synchronized锁的对象,所以wait、notify等必须和对象关联才能配合synchronized使用。线程
方法 | 做用 |
---|---|
wait | 线程自动释放占有的对象锁,并等待notify。 |
notify | 随机唤醒一个正在wait当前对象的线程,并让被唤醒的线程拿到对象锁 |
notifyAll | 唤醒全部正在wait当前对象的线程,可是被唤醒的线程会再次去竞争对象锁。由于一次只有一个线程能拿到锁,全部其余没有拿到锁的线程会被阻塞。推荐使用。 |
接下来咱们就使用wait()、notify()来实现一个生产者、消费者模式。这个也是面试过程当中可能会被问到的地方。至于什么是生产者消费者模式,不明白的同窗请自行百度。
首先是一些基础的代码3d
private static Boolean run = true;//控制是否生产和消费 private static final Integer MAX_CAPACITY = 5;//缓冲区最大数量 private static final LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();//缓冲队列
生产者代码code
/** * 生产者 */ class Producter extends Thread { @Override public void run() { while (run) { synchronized (queue) { while (queue.size() >= MAX_CAPACITY * 2) { try { System.out.println("缓冲队列已满,等待消费"); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { String string = UUID.randomUUID().toString(); queue.put(string); System.out.println("生产:" + string); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } queue.notifyAll();//通知生产者和消费者 } } } }
消费者代码对象
/** * 消费者 */ class Consumer extends Thread { @Override public void run() { while (run) { synchronized (queue) { while (queue.isEmpty()) { try { System.out.println("队列为空,等待生产"); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { System.out.println("消费:" + queue.take()); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } queue.notifyAll();//通知生产者和消费者 } } } }
代码说明继承
一、生产者和消费者都继承了线程Thread,由于wait、notify自己就是线程间通讯使用
二、生产者和消费者都有两层while,外层的while是用来判断是否运行生产者和消费者。内存的while用来判断队列queue是否已满或者为空,若是知足条件,则使得当前线程变成等待状态(等待notify)。
三、内层的条件判断为何用while不用if,缘由是当线程被wait以后,会释放对象锁。当等待的线程被notify以后,必须再次尝试去获取对象锁,若是没有获取到对象锁,那还必须等待,直到拿到对象锁以后才能向后执行。
四、当生产者生产了一个数据或者消费者消费了一个数据以后,使用notifyAll()方法来通知全部等待当前对象锁的线程,可是一次只会有一个等待的线程能拿到锁。
五、咱们使用queue做为锁的对象在不一样线程之间进行通讯
代码运行结果队列
队列为空,等待生产 生产:e422484e-8eb3-4a91-8a7c-97e21d7ef498 生产:7894b802-2529-4798-ba98-9f0657f39240 生产:848f6759-a427-4a94-89dc-3f484daaa467 生产:f711d3dc-972c-4c44-8640-faffe376d354 生产:38a08e62-d774-4ed5-8b51-f1ad534c246f 消费:e422484e-8eb3-4a91-8a7c-97e21d7ef498 消费:7894b802-2529-4798-ba98-9f0657f39240 消费:848f6759-a427-4a94-89dc-3f484daaa467 消费:f711d3dc-972c-4c44-8640-faffe376d354 消费:38a08e62-d774-4ed5-8b51-f1ad534c246f 队列为空,等待生产 生产:9fae26f3-0b6e-4fbd-9620-040667efe0af 生产:95bb1d88-e08a-4f70-a270-f75760994184 消费:9fae26f3-0b6e-4fbd-9620-040667efe0af 消费:95bb1d88-e08a-4f70-a270-f75760994184 队列为空,等待生产 生产:13d304bc-dff3-44a4-9527-2e0facd884e7 生产:2693e069-bae1-4beb-adcd-a3c3bf5d232b 消费:13d304bc-dff3-44a4-9527-2e0facd884e7 消费:2693e069-bae1-4beb-adcd-a3c3bf5d232b
由结果也能够看出来,生产和消费是无规则的,由于虽然notifyAll()通知了全部的等待线程,可是不肯定那个线程中能拿到对象锁。可是也有一个很明显的问题,由于同时只有一个线程能拿到对象锁,生产者和消费者不可能同时运行。