notify()方法、notifyAll()方法和wait()方法

线程作为程序内部的多个执行流,相互之间是可以通讯的。线程间通讯可以通过多种方式来进行,例如:线程间可以共享变量来进行通讯,使每个线程根据共享变量的值进行操作和运算,当通过共享变量进行通讯时,通常需要引入同步控制,
    线程间也可以通过 wati()、notify()和notifyAll()等方法进行通讯。
    每一个类的对象的实例都有一个等待集合,当在该实例上调用wait()方法后,线程都会进入到该实例的等待集合中。
    wait()、notify() 和 notifyAll()方法使类Object中定义的方法,由于Java中的类都是Object类的子类,因此,java语言中任何类都可以调用这些方法,但这些方法更多的使在多线程环境中使用。
    wait()方法:
    wait()方法的调用的一般形式是:
    对象名.wait();
    称作线程在对象上的等待,作用是把当前的线程放入对象的等待集合中。
    wait()方法通常需要放入以synchronized()方法修饰的语句块或方法中,如果在synchronized外部调用wait()方法,运行时刻Java虚拟机会抛出IllegalMonitorStateException异常。
    wait()方法通常被放到try{}catch() 语句块中,例如:
    try{
        wait();
    }catch(InterruptedException e){
        e.printStackTrace();
    }
    当线程调用wait()方法后,Java虚拟机会让当前的线程进入到休眠状态,并释放对对象的同步锁的控制权,允许其他线程执行该同步代码,要唤醒该线程,需要在同一个对象上调用notify()或notifyAll()方法。
    
    notify()方法:
    线程不能一直在等待集合中,必须有方法对其进行唤醒,notify()方法可以对线程进行唤醒。
    notify()方法调用的一般形式如下:
    对象名.notify();
    当使用当前对象时,使用this作为当前对象的引用,故可以直接写成notify();
    当使用某个对象的notify()方法时,将从该对象的等待集合中选择一个等待的线程唤醒,唤醒的线程将从等待集合中删除。

    notifyAll()方法:
    notifyAll()方法会将所有在等待集合中的线程唤醒,但由于所有的被唤醒的线程仍然要去争用synchronized锁,而synchronized锁具有排他性,最终只有一个线程获得该锁,进行执行状态,其他线程仍要继续等待。
    notifyAll()方法调用一般形似如下:
    对象名notifyAll();
    当使用当前对象时,使用this作为当前都对象的引用,故可以直接写成notifyAll();
    notify() 和notifyAll()方法不需要放入try...catch...语句中,主要的区别是:notify是唤醒一个线程,而notifyAll()是唤醒该对象等待集合中的所有线程。
    当只有一个线程等待,另一个线程通知时,建议使用notify(),当有多个线程等待时,建议使用notifyAll()。

Object.wait 中JDK提供的doc文档

1

2

3

4

5

6

7

8

9

10

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. <br>In other words, this method behaves exactly as if it simply performs the call wait(0).

The current thread must own this object's monitor. <br>The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up <br>either through a call to the notify method or the notifyAll method. <br>The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:

           synchronized (obj) {

               while (<condition does not hold>)

                   obj.wait();

               ... // Perform action appropriate to condition

           }

        

This method should only be called by a thread that is the owner of this object's monitor. <br>See the notify method for a description of the ways in which a thread can become the owner of a monitor.

  

一个对象的monitor只能被一个线程占用,wait()方法会释放这个对象的锁, 既然要释放 就先要取得这个锁, 取得对象锁的方式只有synchronized()。释放锁之后, 线程进入BLOCK状态

doc文档中说明调用wait的时机是因为运行条件condition不满足,

比如生产者没有往队列中放东西,队列已经空了。队列不为空的时候,再调用obj.notify()。此时condation==队列,obj是针对这个队列的线程间共享变量,也可以是队列本身(最好不是,会阻塞生产者的线程)?

    Demo示例:
    模拟单缓冲区的生产者和消费者问题。
    分析:单缓冲区是指生产者和消费者之间只有一个缓冲区,故生产者生产一个数据后,即进入等待状态,直到消费者消费,消费者消费一个数据后同样进入等待状态,知道生产者生产数据。

package com.auth;

public class CubbyHole{
    private int goods;
    private boolean empty;

    public CubbyHole( ){
        empty=true;
    }
    //从缓冲区取数据
    public synchronized int get(){
        while(empty){
            try{
                wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println("消费者拿走了物品"+ goods );
        empty =true;
        //此时唤醒生产者线程,抓紧生产
        notify();
        return goods;
    }
    //向缓冲区放入数据
    public synchronized void put(int value){
        while(!empty){
            try{
                //有产品时,就不需要生产,不需要放数据,线程等待
                wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
        goods = value;
        System.out.println("生产者生产了物品"+goods);
        empty=false;
        notify();
    }
}
//生产者Producer
public class Producer extends Thread{
    private CubbyHole cubbyHole;
    public Producer(CubbyHole c){
        this.cubbyHole =c;
    }
    public void run(){
        for(int i=0;i<50;i++){
            cubbyHole.put((int)(100*Math.random()));
        }
    }
}
//消费者Consumer
public class Consumer extends Thread{
    private CubbyHole cubbyHole;
    public Consumer( CubbyHole c){
        this.cubbyHole =c;
    }
    public void run(){
        for(int i=0;i<50;i++){
            cubbyHole.get();
        }
    }
}
//启动类
public class Index{
    public static void main(String [] args){
        CubbyHole c= new CubbyHole();
        Producer  producer = new Producer(c);
        producer.start();
        Consumer consumer = new Consumer(c);
        consumer.start();
    }
}

例子代码:

说明:线程运行3秒后调用wait等待,6秒后主线程调用notify唤醒等待线程。

public class MyThread implements Runnable {
    private Object flag;
    private String threadName;

    public MyThread(Object flag,String threadName) {
        this.flag = flag;
        this.threadName = threadName;
    }

    @Override
    public void run(){
        try{
            for(int i = 0; i < 10;i++){
                if(i == 3){
                    synchronized (this.flag){
                        System.out.println("3秒后线程调用wait睡眠");
                        this.flag.wait();
                    }
                }
                System.out.println(this.threadName + " " + i);
                Thread.sleep(1000);
            }
        } catch(InterruptedException e){
            e.printStackTrace();
        }

    }
}



public class TestMain {
    public static void main(String[] args) {
        Object object = new Object();
        MyThread myThread1 = new MyThread(object,"thread1");
//        MyThread myThread2 = new MyThread(object,"thread2");
        Thread test1 = new Thread(myThread1);
        test1.start();

//        Thread test2 = new Thread(myThread1);
//        test2.start();

        try{
            Thread.sleep(6000);
            System.out.println("6秒后唤醒线程");
            synchronized (object){
                object.notify();
            }
            System.in.read();
        } catch(InterruptedException e){
            e.printStackTrace();
        } catch(IOException e){
            e.printStackTrace();
        }

    }
}

结果:

测试notifyAll(MyThread代码不变)

main方法代码:

import java.io.IOException;

/**
 * Created on 2016/1/31.
 */
public class TestMain {
    public static void main(String[] args) {
        Object object = new Object();
        MyThread myThread1 = new MyThread(object,"thread1");
        MyThread myThread2 = new MyThread(object,"thread2");
        Thread test1 = new Thread(myThread1);
        test1.start();

        Thread test2 = new Thread(myThread2);
        test2.start();

        try{
            Thread.sleep(6000);
            System.out.println("6秒后唤醒线程");
            synchronized (object){
                object.notifyAll();
            }
            System.in.read();
        } catch(InterruptedException e){
            e.printStackTrace();
        } catch(IOException e){
            e.printStackTrace();
        }

    }
}

结果截图:

### 回答1: 在 Java 中,notifynotifyAll 都是用来唤醒等待中的线程的方法notify 方法会随机唤醒一个等待中的线程,而 notifyAll 方法则会唤醒所有等待中的线程。 notify 方法通常用于在多线程中共享资源的情况下,当一个线程修改了共享资源之后,通知其他等待中的线程可以重新获取资源并继续执行。但是,如果有多个线程在等待同一个资源,而只有一个线程被唤醒,那么其他线程仍然会继续等待,因此需要使用 notifyAll 方法来唤醒所有等待中的线程。 需要注意的是,notifynotifyAll 方法必须在同步代码块中使用,即在 synchronized 代码块中调用这两个方法。否则会抛出 IllegalMonitorStateException 异常。另外,notifynotifyAll 方法的使用也要谨慎,如果使用不当可能会导致死锁或活锁等问题。 ### 回答2: 在Java中,多线程的notifynotifyAll都是用于线程间的通信。它们都属于Object类的方法,用于唤醒等待在该对象上的线程。 notify方法用于唤醒在该对象上等待的某个线程。当调用notify时,系统将从该对象的等待池中选择一个线程唤醒,被唤醒的线程将进入就绪状态,等待系统调度执行。 notifyAll方法用于唤醒在该对象上等待的所有线程。当调用notifyAll时,系统将唤醒该对象上的所有线程,被唤醒的线程将进入就绪状态,等待系统调度执行。 需要注意的是,notifynotifyAll只能在同步代码块或同步方法中调用,否则会抛出IllegalMonitorStateException异常。这是因为线程在调用notifynotifyAll时必须先获得该对象的锁,才能进行通知操作。 此外,使用notifynotifyAll时需要注意以下几点: 1. 调用notifynotifyAll后,并不会立即释放锁,而是等待同步代码块或同步方法执行完毕后才会释放锁。 2. notify只能唤醒一个线程,选择唤醒的线程是不确定的,而notifyAll会唤醒所有等待的线程。 3. 使用notifynotifyAll时应该慎重,避免因过度唤醒线程导致性能下降或死锁等问题的发生。 总之,通过notifynotifyAll方法,可以实现多个线程之间的协调和通信,实现线程间的同步操作。但是如果使用不当,可能会导致线程安全性和性能问题,因此需要合理地进行调度和使用。 ### 回答3: 在Java中,notifynotifyAll是两种不同的线程通信机制。 notifynotify方法用于唤醒在对象上等待的单个线程。当某个线程调用某个对象的notify方法时,它会唤醒在该对象上等待的单个线程。如果有多个线程等待该对象,但只有一个线程能被唤醒。选择哪个线程被唤醒是不确定的,取决于操作系统的调度机制。 notifyAll:notifyAll方法用于唤醒在对象上等待的所有线程。当某个线程调用某个对象的notifyAll方法时,它会唤醒在该对象上等待的所有线程。这些线程会竞争锁资源,只有一个线程能够获得该对象的锁,并继续执行。 在使用多线程的场景中,notifynotifyAll方法通常与wait方法配合使用,实现线程间的通信和协调。wait方法用于使线程等待,并释放该对象的锁,进入该对象的等待池。当其他线程调用notifynotifyAll方法时,等待线程才有可能被唤醒,重新进入该对象的锁池,继续执行。 需要注意的是,notifynotifyAll方法必须在获得相关对象的锁之后调用,否则会抛出IllegalMonitorStateException异常。另外,线程调用notify方法只能唤醒一个等待线程,而调用notifyAll方法能唤醒所有等待线程,但哪个线程能够获得锁资源是不确定的。 因此,在进行线程间通信和协调时,根据具体的需求选择使用notify还是notifyAll方法,并确保在正确的时机、正确的对象上调用这些方法,以实现线程间的正确通信和顺序控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值