java线程间通信wait,notify/notifyAll

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014730165/article/details/82023307

1.概述

Java所有的类都继承Object对象。Object对象有以下常用方法:

// 克隆当前对象
protected Object clone() 
// 判断对象内存空间地址是否相同
boolean equals(Object obj) 
// 显示调用对象清理
protected void finalize()
// 获取当前对象的Class对象,可以获取方法区里类的元数据信息
Class<?> getClass() 
// 每个对象都有一个hashCode和内存中的地址相对应,Java集合对象常用
int hashCode() 
// 唤醒当前waiting的线程
void notify() 
// 唤醒所有waiting的线程
void notifyAll() 
// 方便定位错误,给每个成员变量加入到toString()
String toString() 
// 调用wait(),进入等待状态
void wait() 
// 指定当前wait的等待时间
void wait(long timeout)
//它允许更好地控制在放弃之前等待通知的时间量。用毫微秒度量的实际时间量
void wait(long timeout, int nanos) 

2.wait(),notify/notifyAll()方法简介

  • wait()、notify/notifyAll() 方法是Object的本地final方法,无法被重写。
  • wait()使当前线程阻塞,前提是必须先获得锁,一般配合synchronized 关键字使用,在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法。

没有用synchronized修饰run()方法会抛出异常:

public class JavaWait01 implements Runnable {

    @Override
    public void run() {
        System.out.println("hello wait");
        try {
            wait(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("waiting over");
    }

    public static void main(String[] args) {
        JavaWait01 javaWait01 = new JavaWait01();
        Thread thread = new Thread(javaWait01);
        thread.start();
    }
}
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
hello wait
    at java.lang.Object.wait(Native Method)
    at netty.sd.threadpool.JavaWait01.run(JavaWait01.java:9)
    at java.lang.Thread.run(Thread.java:748)

Process finished with exit code 08

添加synchronize修饰 run()方法:

public class JavaWait01 implements Runnable {

    @Override
    public synchronized void run() {
        System.out.println("hello wait");
        try {
            wait(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("waiting over");
    }

    public static void main(String[] args) {
        JavaWait01 javaWait01 = new JavaWait01();
        Thread thread = new Thread(javaWait01);
        thread.start();
    }
}
hello wait
waiting over
Process finished with exit code 0
  • 由于 wait()、notify/notifyAll() 在synchronized 代码块执行,说明当前线程一定是获取了锁的
当线程执行wait()方法时候,会释放当前的锁,然后释放CPU,进入等待状态。
只有当notify/notifyAll()被执行时候,才会唤醒一个或多个正处于等待状态
的线程,然后继续往下执行,直到执行完synchronized代码块的代码或是中途遇到wait() ,再次释放锁,
也就是说,notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具
体执行情况。所以在编程中,尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程
  • wait() 需要被try catch包围,中断也可以使wait等待的线程唤醒
public class JavaWait02 implements Runnable {

    @Override
    public synchronized void run() {
        System.out.println("hello wait");
        try {
            wait();
        } catch (InterruptedException e) {
            System.out.println("wait 被中断");
            e.printStackTrace();
        }
        System.out.println("waiting over");
    }

    public static void main(String[] args) {
        JavaWait02 javaWait01 = new JavaWait02();
        Thread thread = new Thread(javaWait01);
        thread.start();

        thread.interrupt();
    }
}
hello wait
java.lang.InterruptedException
wait 被中断
    at java.lang.Object.wait(Native Method)
waiting over
    at java.lang.Object.wait(Object.java:502)
    at netty.sd.threadpool.JavaWait02.run(JavaWait02.java:9)
    at java.lang.Thread.run(Thread.java:748)
  • notify 和wait 的顺序不能错,如果A线程先执行notify方法,B线程在执行wait方法,那么B线程是无法被唤醒的。
  • notify 和 notifyAll的区别:
    notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll 方法。比如在生产者-消费者里面的使用,每次都需要唤醒所有的消费者或是生产者,以判断程序是否可以继续往下执行。
  • 在多线程中要测试某个条件的变化,使用if 还是while:
    当wait()被唤醒以后,会接着原来执行的地方接着执行下去。那么如果使用if()语句进行判断的话,只会进行一次条件判断。程序接着执行下去。可能存在这种情况,当前线程虽然被换醒了,依然存在没有达到执行条件的问题。如果使用while()进行条件判断的话,不管wait()是否被唤醒,都会进行条件判断,直到当前wait()被唤醒且while()的条件满足。

3.生产者消费者代码实现

3.1 生产者代码实现:
public class Producer implements Runnable {
    private LinkedList<Integer> resources;
    /*当前仓库最大容量*/
    private int maxSize;

    /*需要生产的产品总数*/
    private int proSize;

    public Producer(LinkedList<Integer> resources, int maxSize, int proSize) {
        this.resources = resources;
        this.maxSize = maxSize;
        this.proSize = proSize;
    }

    @Override
    public void run() {
        synchronized (resources) {
            for (int i = 0; i < proSize; i++) {
                // 当生产的产品总数大于仓库容量时,进行阻塞
                while (resources.size() > maxSize) {
                    System.out.println("当前仓库已满,等待消费...");
                    try {
                        // 进入阻塞状态
                        resources.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("已经生产产品数:" + i + "\t现仓储量总量:" + resources.size());
                resources.add(i);
                // 唤醒所有消费者
                resources.notifyAll();
                // 唤醒单个消费者
                //resources.notify();
            }
        }
    }
}
3.2 消费者代码实现:
public class Consumer implements Runnable {
    private LinkedList<Integer> resources;

    public Consumer(LinkedList<Integer> resources) {
        this.resources = resources;
    }

    @Override
    public void run() {
        String threadName = "thread_name" + Thread.currentThread().getName();
        while (true) {
            synchronized (resources) {
                // 当前仓库没有产品的时候,进行阻塞
                while (resources.size() <= 0) {
                    System.out.println(threadName + " 当前仓库没有产品,请稍等...");
                    try {
                        // 进入阻塞状态
                        resources.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 消费数据
                int size = resources.size();
                for (int i = 0; i < size; i++) {
                    Integer remove = resources.remove();
                    System.out.println(threadName + " 当前消费产品编号为:" + remove);
                }
                // 唤醒生产者
                resources.notify();
            }
        }
    }
}
3.3 测试用例:
public class ProducerConsumerTest {
    public static void main(String[] args) throws InterruptedException {
        LinkedList<Integer> integers = new LinkedList<>();
        Producer producer = new Producer(integers, 10, 20);
        Consumer consumer = new Consumer(integers);

        Thread producerThread = new Thread(producer);
        Thread consumer1thread = new Thread(consumer, "consumer_1");
        Thread consumer2thread = new Thread(consumer, "consumer_2");

        consumer2thread.start();
        consumer1thread.start();
        TimeUnit.SECONDS.sleep(2);
        producerThread.start();
    }
}
3.4 输出结果:
thread_nameconsumer_1 当前仓库没有产品,请稍等...
thread_nameconsumer_2 当前仓库没有产品,请稍等...
已经生产产品数:0   现仓储量总量:0
已经生产产品数:1   现仓储量总量:1
已经生产产品数:2   现仓储量总量:2
已经生产产品数:3   现仓储量总量:3
已经生产产品数:4   现仓储量总量:4
已经生产产品数:5   现仓储量总量:5
已经生产产品数:6   现仓储量总量:6
已经生产产品数:7   现仓储量总量:7
已经生产产品数:8   现仓储量总量:8
已经生产产品数:9   现仓储量总量:9
已经生产产品数:10  现仓储量总量:10
当前仓库已满,等待消费...
thread_nameconsumer_2 当前消费产品编号为:0
thread_nameconsumer_2 当前消费产品编号为:1
thread_nameconsumer_2 当前消费产品编号为:2
thread_nameconsumer_2 当前消费产品编号为:3
thread_nameconsumer_2 当前消费产品编号为:4
thread_nameconsumer_2 当前消费产品编号为:5
thread_nameconsumer_2 当前消费产品编号为:6
thread_nameconsumer_2 当前消费产品编号为:7
thread_nameconsumer_2 当前消费产品编号为:8
thread_nameconsumer_2 当前消费产品编号为:9
thread_nameconsumer_2 当前消费产品编号为:10
thread_nameconsumer_2 当前仓库没有产品,请稍等...
thread_nameconsumer_1 当前仓库没有产品,请稍等...
已经生产产品数:11  现仓储量总量:0
已经生产产品数:12  现仓储量总量:1
已经生产产品数:13  现仓储量总量:2
已经生产产品数:14  现仓储量总量:3
已经生产产品数:15  现仓储量总量:4
已经生产产品数:16  现仓储量总量:5
已经生产产品数:17  现仓储量总量:6
已经生产产品数:18  现仓储量总量:7
已经生产产品数:19  现仓储量总量:8
thread_nameconsumer_1 当前消费产品编号为:11
thread_nameconsumer_1 当前消费产品编号为:12
thread_nameconsumer_1 当前消费产品编号为:13
thread_nameconsumer_1 当前消费产品编号为:14
thread_nameconsumer_1 当前消费产品编号为:15
thread_nameconsumer_1 当前消费产品编号为:16
thread_nameconsumer_1 当前消费产品编号为:17
thread_nameconsumer_1 当前消费产品编号为:18
thread_nameconsumer_1 当前消费产品编号为:19
thread_nameconsumer_1 当前仓库没有产品,请稍等...
thread_nameconsumer_2 当前仓库没有产品,请稍等...

猜你喜欢

转载自blog.csdn.net/u014730165/article/details/82023307