Object的wait()方法和notify()方法

JDK中关于wait()方法和notify()方法说明英文版翻译,渣渣英语,如有不妥,望提出宝贵意见~

public final void wait() throws InterruptedException

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. 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. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object’s monitor to wake up either through a call to the notify method or the notifyAll method. 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. See the notify method for a description of the ways in which a thread can become the owner of a monitor.
致使当前线程等待直到另一个线程调用notify()方法或者notifyAll()方法。换句话说,这个方法就像是调用wait(0).
当前线程必须拥有该对象的监视器(也就是锁,以下全部称为锁。通过这句话,也可以说明该方法必须用在有synchronized修饰的方法或者代码块中)。线程放弃锁的拥有权并且等待,直到另一个线程通过调用notify()方法或者notifyAll()方法去唤醒等待该对象锁的线程。线程等待直到重新获得锁的拥有权,然后重新获得执行的机会。
有一种说法,中断和虚假的唤醒也是可能得,这个方法必须总是用在一个循环中,如下:

synchronized (obj) {
         while (<condition does not hold>)
             obj.wait();
         ... // Perform action appropriate to condition
     }

这个方法应该只能被一个线程所拥有的“锁对象”调用。

public final void notify()

Wakes up a single thread that is waiting on this object’s monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object’s monitor by calling one of the wait methods.
The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object.

This method should only be called by a thread that is the owner of this object’s monitor. A thread becomes the owner of the object’s monitor in one of three ways:

By executing a synchronized instance method of that object.
By executing the body of a synchronized statement that synchronizes on the object.
For objects of type Class, by executing a synchronized static method of that class.
Only one thread at a time can own an object’s monitor.
唤醒一个正在等待锁对象的单线程。如果有很多现线程等待该对象锁,那么其中的一个线程会被唤醒。选择是随意的,而且在执行的自由裁量权下进行的。一个线程通过调用wait()方法从而实现等待“锁对象”。
被唤醒的线程将有可能不会继续执行直到当前线程放弃该“锁对象”。被唤醒线程将以通常的方式与可能在该对象上进行同步竞争的任何其他线程进行竞争;例如,唤醒线程在“锁对象”的下一持有线程中没有任何优先权和优势。
这个方法只能被持有“锁对象”的线程调用。一个线程称为“锁对象”的持有者有三种方式:
1.通过执行对象实例的synchronized方法
2.通过执行对象中的syncronized代码块
3.对于类型类的对象,通过执行该类的同步静态方法。


注意:wait方法和notify方法总是成对出现的

写个示例:一个线程将数字增加,一个线程将数字减少,使得数字0和1交替出现。
思路:数字增加和数字减少的两个方法需要实现同步。如果增加的线程检测到数字不为0,则等待;否则数字增加同时唤醒等待的线程;如果减少的线程检测到数字不为1,则等待;否则 数字减少同时唤醒等待的线程。

public class Example {
    private int number;
    /**
     * 数字+1方法:如果不为0,则等待;否则+1,输出结果后,唤醒等待的线程
     */
    public synchronized void increase(){
        if(0 != number){  
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number++;
        System.out.println(number);
        notify();
    }
    /**
     * 数字-1的方法:如果为0,则等待;否则-1,输出结果后,唤醒等待的线程
     */
    public synchronized void decrease(){
        if(0 == number){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number -- ;
        System.out.println(number);
        notify();
    }
}
/**
 * 数字增加的线程:传入对象,同时在run方法中调用对象的increase方法
 */
public class IncreaseThread extends Thread {
    private Example example;
    public IncreaseThread(Example example){
        this.example = example;
    }
    public void run(){
        for(int i = 0;i < 20;i ++){
            try {
                Thread.sleep((long)Math.random() * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            example.increase();
        }
    }
}
/**
 * 数字减少的线程:传入对象,同时在run方法中调用对象的decrease方法
 */
public class DecreaseThread extends Thread {
    private Example example;
    public DecreaseThread(Example example){
        this.example = example;
    }
    public void run(){
        for(int i = 0;i < 20;i ++){
            try {
                Thread.sleep((long)Math.random() * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            example.decrease();
        }
    }
}
/**
 * main方法使用上述所写的线程
 */
public class Main {
    public static void main(String[] args){
        Example example = new Example();

        Thread t1 = new IncreaseThread(example);
        Thread t2 = new DecreaseThread(example);

        t1.start();
        t2.start();
    }
}

输出结果:
1
0
1
0
1
0
……
1和0是交替出现20次。分析:number初始值为0,所以IncreaseThread线程首先执行一次increase()方法,唤醒可能阻塞的线程DecreaseThread,或者继续执行循环increase()方法,但是由于此时number值变为1,所以increase方法无法执行,只能是DecreaseThread线程执行,从而执行decrease()方法,number减1,此时number值变为0,继续上述类似的流程,总之结果是1和0交替出现。


在上述示例中,我们开启了2个线程,一个用于增加,一个用于减少。下面我们开启4个线程,两个用于增加,两个用于减少

public class Main {
    public static void main(String[] args){
        Example example = new Example();

        Thread t1 = new IncreaseThread(example);
        Thread t2 = new DecreaseThread(example);

        Thread t3 = new IncreaseThread(example);
        Thread t4 = new DecreaseThread(example);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

结果发现输出结果不是我们预想的0和1交替,而是出现了负数或者大于1的数(可能每一次运行的结果都不尽相同),这是为什么呢?我们不是已经通过notify和wait方法实现同步了吗?
下面来分析一下原因:在第一种情景下,只有两个线程,一个增加,一个减少,假设当增加的线程进入等待状态,放弃持有的锁对象,那么此时只可能是减少的线程去获得该对象的锁,从而执行数字减少的操作,执行结束后唤醒阻塞的增减线程,所以会出现预想的结果:1和0交替出现。但是,在第二种情景下,有四个线程,两个增加,两个减少,依次称为线程1,2,3,4。假设1线程首先执行,线程2,3,4都在等待(wait方法执行后,处于等待的状态)。此时number变为1,输出1,notify方法调用通知所有等待的线程,此时线程4获得该对象的锁,则开始执行,number变0,输出0,之后线程4执行,number变为0,输出0,同时通知所有等待的线程,此时线程3获得该对象的锁,继续执行,则number变为-1!….此后,number变成何值已不是我们所能预料的了。
错误出现的根本原因在于线程在wait方法进入等待状态到通过notify方法被唤醒后,number的状态已经发生了改变,但是唤醒后并没有进行状态的判断而是直接去执行了操作,这是产生错误的根本原因!所以我们需要对上述代码进行修改,线程中if条件的判断改为while循环判断,即当该线程被唤醒后,需要继续去判断number的状态,如果满足条件则执行增加或者减少的操作,否则还是需要wait继续等待!

public class Example {
    private int number;
    /**
     * 数字+1方法:如果不为0,则等待;否则+1,输出结果后,唤醒等待的线程
     */
    public synchronized void increase(){
        while (0 != number){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number++;
        System.out.println(number);
        notify();
    }
    /**
     * 数字-1的方法:如果为0,则等待;否则-1,输出结果后,唤醒等待的线程
     */
    public synchronized void decrease(){
        while(0 == number){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number -- ;
        System.out.println(number);
        notify();
    }
}

总结:wait方法和notify方法都定义在Object类中,而且使final的,因此会被所有的Java类所继承且无法重写。这两个方法要求在调用时,线程已经获得了对象的锁,因此对这两个方法的调用需要在synchrnoized方法或块中。当现成执行了wait方法时,它会释放掉对象的锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值