等待/通知的相关方法式任意java对象都具备的,notify,wait方法被定义在java.lang.Object,都是final,不可重写
,是实例方法,新手比较容易出错的地方是,调用时候必须调用 锁对象.wait()/锁对象.nofity(),并且只能在临界区中调用
具体了解下,notify,wait定义
名称 | 描述 |
notify() | 通知在同一个对象上等待的线程,使其从wait()方法返回,而返回的前提是该线程获取到对象的锁 |
notifyAll() | 通知所有等待同一个对象锁的线程 |
wait() | 调用该方法的线程进入WAITING状态,并且释放对象的锁,从方法描述上可以看出是响应中断的/或者接收到notify才会返回 |
wait(long) | 超时等待一段时间,没有接受到通知就超时返回 |
wait(long,int) | 对于超时时间更细粒度的控制,可以达到纳秒 |
等待/通知机制,举个例子来说线程A调用对象obj的wait方法进入WAITING状态,另一个线程调用同一个对象的notify方法唤醒线程A,线程A继续执行wait后的程序
简单的案例 ,单个生产者、单个消费者
锁对象:
package com.ftf.thread.test;
public class Student {
public volatile boolean flag;
public Student(boolean flag) {
this.flag = flag;
}
}
生产者:
package com.ftf.thread.test;
public class Product {
private Student obj;
public Product(Student obj) {
this.obj = obj;
}
public void dothis() {
synchronized (obj) {
try{
if(!obj.flag) {
obj.wait();
}
System.out.println("我是生产者线程"
+ Thread.currentThread().getName());
Thread.sleep(1000);
obj.flag = false;
obj.notify();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
消费者线程:
package com.ftf.thread.test;
public class Customer {
private Student obj;
public Customer(Student obj) {
this.obj = obj;
}
public void dothis() {
synchronized (obj) {
try {
if(obj.flag) {
obj.wait();
}
System.out.println("我是消费者线程"
+ Thread.currentThread().getName());
Thread.sleep(1000);
obj.flag = true;
obj.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
main方法:
package com.ftf.thread.test;
public class NotifyWaitDemo {
public static void main(String[] args) {
Student obj = new Student(true);
Product notify = new Product(obj);
Customer wait = new Customer(obj);
Thread1 t1 = new Thread1(notify);
Thread2 t4 = new Thread2(wait);
t1.start();
t4.start();
}
}
class Thread1 extends Thread{
private Product notify;
public Thread1(Product notify){
this.notify= notify;
}
@Override
public void run() {
while(true){
notify.dothis();
}
}
}
class Thread2 extends Thread{
private Customer wait;
public Thread2(Customer wait){
this.wait = wait;
}
@Override
public void run() {
while(true){
wait.dothis();
}
}
}
说明:
1.通过类Student flag定义为volatile这样,通过内存可见性,两个线程修改这个值,另一个线程都能看到,来控制两个线程释放锁和获取锁交互
2. 在单个消费者, 单个生产者和使用notify的情况下,使用if判断没有问题
多个生产者,多个消费者需要使用while作为条件判断
验证在多生产者消费者情况下,使用if存在的问题
生产者dothis方法修改: while->if,因为是多个生产者消费者,需要将notify->notifyAll
public void dothis() {
synchronized (obj) {
try{
if (!obj.flag) {
obj.wait();
}
System.out.println("我是生产者线程"
+ Thread.currentThread().getName());
Thread.sleep(1000);
obj.flag = false;
obj.notifyAll();
}catch(InterruptedException e){
e.printStackTrace();
}
}
同样,修改消费者代码
public void dothis() {
synchronized (obj) {
try {
if (obj.flag) {
obj.wait();
}
System.out.println("我是消费者线程"
+ Thread.currentThread().getName());
Thread.sleep(1000);
obj.flag = true;
obj.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
,修改main方法:
public static void main(String[] args) {
Student obj = new Student(true);
Product notify = new Product(obj);
Customer wait = new Customer(obj);
Thread1 t1 = new Thread1(notify);
Thread1 t2 = new Thread1(notify);
Thread2 t3 = new Thread2(wait);
Thread2 t4 = new Thread2(wait);
t1.start();
t2.start();
t3.start();
t4.start();
}
测试结果:
出现了,消费者线程连续消费两次的情况。
说明:
在多生产者,多消费者情况下,对于wait方法的判断使用while条件判断,而不是使用if,
是为了防止在多生产者,多消费者的情况下,如果使用if的条件,当前线程释放锁后,再次获取到锁后,
不进行检查flag,直接往下执行,那本例来说,就是消费者线程释放掉锁后,再次获取到锁,再次消费