java thread中的wait()和notify()

关于线程的状态

java thread有五种状态类型

  1. 新建状态(New):新创建了一个线程对象。
  2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
  3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
  4. 阻塞状态(Blocked):塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
  5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

当我们调用线程类的sleep()、suspend()、yield()、wait()等方法时会导致线程进入阻塞状态。

了解更详细信息可以参考:java 线程详解

关于wait()和notify()

  • wait(): 调用任何对象的wait()方法会让当前线程进入等待,直到另一个线程调用同一个对象的notify()或notifyAll()方法。
  • notify():唤醒因调用这个对象wait()方法而阻塞的线程。

首先,sleep()、suspend()、yield ()等方法都隶属于 Thread 类,但wait()/notify()这一对却直接隶属于Object 类,也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。而调用对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException异常。

最后,关于 wait() 和 notify() 方法再说明三点:

  1. 调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题
  2. 除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。
  3. wait()和notify()必须成对存在。

举例说明

例子一:主线程等待计数线程计算结束后打印计算结果

先写一个计数线程类CountThread:

public class CountThread extends Thread{
    int total;
    @Override
    public void run(){
        synchronized (this){
            for(int i=0;i<100;i++){
                total=total+1;
            }
             this.notify();//唤醒被阻塞的线程
        }
    }
}

再写一个测试类作为主线程:

public class TestWait {
    public static void main(String[] args) {
        CountThread countThread=new CountThread();
        countThread.start();
        synchronized (countThread){
            System.out.println("等到countThread线程计算结束...");
            try {
                countThread.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("计算的结果是:"+countThread.total);
        }
    }
}

运行结果:

等到countThread线程计算结束...
计算的结果是:100

实例二:生产者、消费者

生产者:

public class Producer extends Thread {
    public static final int MAX_BOX_SIZE = 5;
    Vector<String> messageBox = new Vector<>();

    @Override
    public void run() {
        try {
            while (true) {
                putMessage();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void putMessage() throws InterruptedException {
        while (messageBox.size() == MAX_BOX_SIZE) {
            this.wait();//当箱子满后则进入等待
        }
        messageBox.add(new Date().toString());
        System.out.println("放入一条消息"+new Date().toString());
        this.notify();//放入消息后唤醒被锁住的线程(取消息线程)
    }

    public synchronized void getMessage() throws InterruptedException {
        while (messageBox.size() == 0) {
            this.wait();//当箱子空后进入等待
        }
        String message = (String) messageBox.firstElement();
        messageBox.removeElement(message);
        System.out.println("取出一条消息"+message);
        this.notify();//删除消息后唤醒被锁住的线程(放消息线程)
    }
}

消费者:

public class Consumer extends Thread{
    Producer producer;
    public Consumer(Producer producer){
        this.producer=producer;
    }
    @Override
    public void run() {
        try {
            while (true) {
                producer.getMessage();
                Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Producer producer=new Producer();
        Consumer consumer=new Consumer(producer);
        producer.start();
        consumer.start();
    }
}

打印结果:

放入一条消息Thu Jul 07 16:44:41 CST 2016
放入一条消息Thu Jul 07 16:44:41 CST 2016
放入一条消息Thu Jul 07 16:44:41 CST 2016
放入一条消息Thu Jul 07 16:44:41 CST 2016
放入一条消息Thu Jul 07 16:44:41 CST 2016
取出一条消息Thu Jul 07 16:44:41 CST 2016
取出一条消息Thu Jul 07 16:44:41 CST 2016
取出一条消息Thu Jul 07 16:44:41 CST 2016
时间: 2025-01-23 07:58:21

java thread中的wait()和notify()的相关文章

Java多线程中的wait与notify,notifyall例子

在Java多线程编程中,wait()的作用的是让当前线程进入阻塞状态,notify()是让当前线程唤醒继续执行.虽然是对线程状态的控制,但它们其实都是Object中的方法,这是因为wait与notify所起的作用与线程间的互斥锁有关. 在执行wait()和notify()之前,必须要先获得互斥锁,即一定要和synchronized一起使用.wait()的含义是让出获得的互斥锁,并让自己进入阻塞状态.在notify()的时候也已经获得了互斥锁,所做的事情就是唤醒当前线程继续执行. 假如synchr

Java Thread中的属性和方法

手撸一次,加深印象,立此存照. ThreadB package demo.thread; public class ThreadB implements Runnable { public void run() { try { Thread.sleep(5000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("This is thread B."); Thread curT

Java中使用wait()与notify()实现线程间协作

使用wait()与notify()/notifyAll()可以使得多个任务之间彼 此协作. 1. wait()与notify()/notifyAll() 调用sleep()和yield()的时候锁并没有被释放,而调用wait() 将释放锁.这样另一个任务(线程)可以获得当前对象的锁,从而 进入它的synchronized方法中.可以通过notify()/notifyAll(), 或者时间到期,从wait()中恢复执行. 只能在同步控制方法或同步块中调用wait().notify()和 notif

如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例

wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait.notify 和 notifyAll 来实现线程间的通信..举个例子,如果你的Java程序中有两个线程--即生产者和消费者,那么生产者可以通知消费者,让消费者开始消耗数据,因为队列缓冲区中有内容待消费(不为空).相应的,消费者可以通知生产者可以开始生成更多的数据,因为当它消耗掉某些数据后缓冲区不再

Java Thread Programming 1.8.2 - Inter-thread Communication

  Missed Notification A missed notification occurs when threadB tries to notify threadA, but threadA is not yet waiting for the notification. In a multithreaded environment like Java, you don't have much control over which thread runs and for how lon

Java 程序中的多线程

程序|多线程 在Java程序中使用多线程要比在 C 或 C++ 中容易得多,这是因为 Java 编程语言提供了语言级的支持.本文通过简单的编程示例来说明 Java 程序中的多线程是多么直观.读完本文以后,用户应该能够编写简单的多线程程序. 为什么会排队等待? 下面的这个简单的 Java 程序完成四项不相关的任务.这样的程序有单个控制线程,控制在这四个任务之间线性地移动.此外,因为所需的资源 - 打印机.磁盘.数据库和显示屏 -- 由于硬件和软件的限制都有内在的潜伏时间,所以每项任务都包含

Java Thread Status(转)

public static enum Thread.State  extends Enum<Thread.State>线程状态.线程可以处于下列状态之一: 1.NEW 至今尚未启动的线程的状态. 2.RUNNABLE 可运行线程的线程状态.        处于可运行状态的某一线程正在 Java 虚拟机中运行,但它可能正在等待操作系统中的其他资源,比如处理器. 3.BLOCKED 受阻塞并且正在等待监视器锁的某一线程的线程状态.        处于受阻塞状态的某一线程正在等待监视器锁,以便进入一

详解Java编程中线程的挂起、恢复和终止的方法_java

有时,线程的挂起是很有用的.例如,一个独立的线程可以用来显示当日的时间.如果用户不希望用时钟,线程被挂起.在任何情形下,挂起线程是很简单的,一旦挂起,重新启动线程也是一件简单的事. 挂起,终止和恢复线程机制在Java 2和早期版本中有所不同.尽管你运用Java 2的途径编写代码,你仍需了解这些操作在早期Java环境下是如何完成的.例如,你也许需要更新或维护老的代码.你也需要了解为什么Java 2会有这样的变化.因为这些原因,下面内容描述了执行线程控制的原始方法,接着是Java 2的方法. Jav

详解Java编程中线程同步以及定时启动线程的方法_java

使用wait()与notify()实现线程间协作 1. wait()与notify()/notifyAll()调用sleep()和yield()的时候锁并没有被释放,而调用wait()将释放锁.这样另一个任务(线程)可以获得当前对象的锁,从而进入它的synchronized方法中.可以通过notify()/notifyAll(),或者时间到期,从wait()中恢复执行. 只能在同步控制方法或同步块中调用wait().notify()和notifyAll().如果在非同步的方法里调用这些方法,在运