java之wait()、notify()实现非阻塞的生产者和消费者

简介: java之wait()、notify()实现非阻塞的生产者和消费者

一、对于wait()和notify()的解释

    void notify()
    Wakes up a single thread that is waiting on this object’s monitor.
    唤醒等待获取锁资源的单个线程
    void notifyAll()
    Wakes up all threads that are waiting on this object’s monitor.
    唤醒等待获取锁资源的所有线程
    void wait( )
    Causes the current thread to wait until another thread invokes the notify() method or the notifyAll( ) method for this object.
    释放出对象的锁资源,程序会阻塞在这里

二、使用wait()、notify()、notifyAll()应该要注意的地方

 1、wait( ),notify( ),notifyAll( )都不属于Thread类,属于Object基础类,每个对象都有wait( ),notify( ),notifyAll( ) 的功能,因为每个对象都有锁


  2、当需要调用wait( ),notify( ),notifyAll( )的时候,一定都要在synchronized里面,不然会报 IllegalMonitorStateException 异常,可以这样理解,在synchronized(object) {}里面的代码才能获取到对象的锁。


 3、while循环里而不是if语句下使用wait,这样,会在线程暂停恢复后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知


 4、调用obj.wait( )释放了obj的锁,程序就会阻塞在这里,如果后面被notify()或者notifyAll()方法呼唤醒了之后,那么程序会接着调用obj.wait()后面的程序。


   5、notify()唤醒等待获取锁资源的单个线程,notifyAll( )唤醒等待获取锁资源的所有线程


 6、当调用obj.notify/notifyAll后,调用线程依旧持有obj锁,其它线程仍无法获得obj锁,直到调用线程退出synchronized块或者在原有的obj调用wait释放锁,其它的线程才能起来

三、使用wait()、notify()、notifyAll()实现非阻塞式的消费者和生产者

package wait;
import java.util.PriorityQueue;
public class WaitAndNofityTest {
  public static final int COUNT = 5;
  //优先队列,消费者在这个队列里面消耗数据,生产者在这个里面生产数据
  public PriorityQueue<Integer> queue = new PriorityQueue<Integer>(COUNT);
  public static void main(String[] args) {
    WaitAndNofityTest waitAndNofityTest = new WaitAndNofityTest();
    Cus cus = waitAndNofityTest.new Cus();
    Make make = waitAndNofityTest.new Make();
    make.start();
    cus.start();
  }
  //消费者线程类
  class Cus extends Thread {
    @Override
    public void run() {
      System.out.println("Cus queue size is:" + queue.size());
      eatData();
    }
    public void eatData() {
      while (true) {
        synchronized (queue) {
          System.out.println("go to eatData method size is:" + queue.size());
          while (queue.size() == 0) {
            try {
              System.out.println("Cus start notify");
              queue.notify();
              System.out.println("Cus start wait");
              queue.wait();
              System.out.println("Cus wait after");
            } catch (InterruptedException e) {
              queue.notify();
            }
          }
          queue.poll();
          System.out.println("Cus eatData after size is:" + queue.size());
        }
      }
    }
  }
  //生产者线程类
  class Make extends Thread {
    @Override
    public void run() {
      System.out.println("Make queue size is:" + queue.size());
      addData();
    }
    public void addData() {
      while (true) {
        synchronized (queue) {
          System.out.println("go to addData method size is:" + queue.size());
          while (queue.size() == COUNT) {
            try {
              System.out.println("Make start notify");
              queue.notify();
              System.out.println("Make start wait");
              queue.wait();
              System.out.println("Make wait after");
            } catch (InterruptedException e) {
              queue.notify();
            }
          }
          queue.offer(5);
          System.out.println("Cus addData after size is:" + queue.size());
        }
      }
    }
  }
}

四、部分运行结果

Cus addData after size is:5
go to addData method size is:5
Make start notify
Make start wait
Cus wait after
Cus eatData after size is:4
go to eatData method size is:4
Cus eatData after size is:3
go to eatData method size is:3
Cus eatData after size is:2
go to eatData method size is:2
Cus eatData after size is:1
go to eatData method size is:1
Cus eatData after size is:0
go to eatData method size is:0
Cus start notify
Cus start wait
Make wait after
Cus addData after size is:1
go to addData method size is:1
Cus addData after size is:2
go to addData method size is:2
Cus addData after size is:3
go to addData method size is:3
Cus addData after size is:4
go to addData method size is:4
Cus addData after size is:5
go to addData method size is:5
Make start notify
Make start wait
Cus wait after
Cus eatData after size is:4
go to eatData method size is:4
Cus eatData after size is:3
go to eatData method size is:3
Cus eatData after size is:2
go to eatData method size is:2
Cus eatData after size is:1
go to eatData method size is:1
Cus eatData after size is:0
go to eatData method size is:0
Cus start notify
Cus start wait
Make wait after
Cus addData after size is:1
go to addData method size is:1
Cus addData after size is:2
go to addData method size is:2
Cus addData after size is:3
go to addData method size is:3
Cus addData after size is:4
go to addData method size is:4
Cus addData after size is:5
go to addData method size is:5
Make start notify
Make start wait
Cus wait after
Cus eatData after size is:4
go to eatData method size is:4
Cus eatData after size is:3
go to eatData method size is:3
Cus eatData after size is:2
go to eatData method size is:2
Cus eatData after size is:1
go to eatData method siz

如果运行了不理解,你可以自己测试然后分析打印的数据,当然你也可以注释掉2个类里面的任意一个notify方法,然后看是什么效果,是不是和自己分析的结果一样,同样你也可以去掉第一类的wait方法试下,看执行notify方法后是synchronized执行完了呼唤其其它线程或则在调用wait方法之后释放锁后是否也会呼唤其线程?


五、用ArrayBlockingQueue实现阻塞式的生产者和消费者

package wait;
import java.util.concurrent.ArrayBlockingQueue;
public class BlockTest {
    private int COUNT = 5;
    private ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(COUNT);
    public static void main(String[] args)  {
      BlockTest test = new BlockTest();
      Make make = test.new Make();
        Cus cus = test.new Cus();
        make.start();
        cus.start();
    }
    class Cus extends Thread {
        @Override
        public void run() {
            eatData();
        }
        private void eatData() {
            while (true) {
                try {
                    queue.take();
                    System.out.println("队列取一个元素,队列剩余"+queue.size() + "元素");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    class Make extends Thread {
        @Override
        public void run() {
          addData();
        }
        private void addData() {
            while(true){
                try {
                    queue.put(5);
                    System.out.println("队列插入一个元素,队列剩余空间:" + (COUNT-queue.size()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

六、部分运行结果

队列取一个元素,队列剩余4元素
队列取一个元素,队列剩余3元素
队列取一个元素,队列剩余2元素
队列取一个元素,队列剩余1元素
队列取一个元素,队列剩余0元素
队列插入一个元素,队列剩余空间:4
队列插入一个元素,队列剩余空间:3
队列插入一个元素,队列剩余空间:2
队列插入一个元素,队列剩余空间:1
队列插入一个元素,队列剩余空间:0
队列取一个元素,队列剩余4元素
队列取一个元素,队列剩余3元素
队列取一个元素,队列剩余2元素
队列取一个元素,队列剩余1元素
队列取一个元素,队列剩余0元素
队列插入一个元素,队列剩余空间:4
队列插入一个元素,队列剩余空间:3
队列插入一个元素,队列剩余空间:2
队列插入一个元素,队列剩余空间:1
队列插入一个元素,队列剩余空间:0
队列取一个元素,队列剩余4元素
队列取一个元素,队列剩余3元素
队列取一个元素,队列剩余2元素
队列取一个元素,队列剩余1元素
队列取一个元素,队列剩余0元素
队列插入一个元素,队列剩余空间:4
队列插入一个元素,队列剩余空间:3
队列插入一个元素,队列剩余空间:2
队列插入一个元素,队列剩余空间:1
队列插入一个元素,队列剩余空间:0
队列取一个元素,队列剩余4元素
队列取一个元素,队列剩余3元素
队列取一个元素,队列剩余2元素
队列取一个元素,队列剩余1元素
队列取一个元素,队列剩余0元素
队列插入一个元素,队列剩余空间:4
队列插入一个元素,队列剩余空间:3
队列插入一个元素,队列剩余空间:2
队列插入一个元素,队列剩余空间:1
队列插入一个元素,队列剩余空间:0
队列取一个元素,队列剩余4元素
队列取一个元素,队列剩余3元素
队列取一个元素,队列剩余2元素
队列取一个元素,队列剩余1元素
队列取一个元素,队列剩余0元素
队列插入一个元素,队列剩余空间:4
队列插入一个元素,队列剩余空间:3
队列插入一个元素,队列剩余空间:2
队列插入一个元素,队列剩余空间:1
队列插入一个元素,队列剩余空间:0
队列取一个元素,队列剩余4元素
队列取一个元素,队列剩余3元素
队列取一个元素,队列剩余2元素
队列取一个元素,队列剩余1元素
队列取一个元素,队列剩余0元素
队列插入一个元素,队列剩余空间:4
队列插入一个元素,队列剩余空间:3
队列插入一个元素,队列剩余空间:2
队列插入一个元素,队列剩余空间:1
队列插入一个元素,队列剩余空间:0
队列取一个元素,队列剩余4元素
队列取一个元素,队列剩余3元素
队列取一个元素,队列剩余2元素
队列取一个元素,队列剩余1元素
队列取一个元素,队列剩余0元素
队列插入一个元素,队列剩余空间:4
队列插入一个元素,队列剩余空间:3
队列插入一个元素,队列剩余空间:2
队列插入一个元素,队列剩余空间:1
队列插入一个元素,队列剩余空间:0
队列取一个元素,队列剩余4元素


相关文章
|
算法 Java Linux
每日一面 - java里的wait()和sleep()的区别有哪些?
每日一面 - java里的wait()和sleep()的区别有哪些?
每日一面 - java里的wait()和sleep()的区别有哪些?
|
Java 调度
java中wait和sleep的区别
java中wait和sleep的区别
|
11天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
20天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
7天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
27 9
|
10天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
7天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
10天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
24 3
|
9天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
10天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
21 1