You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

notify方法放置位置异常:run方法调用失效,deposit方法调用正常

问题:wait()无法被run方法中notify()唤醒的原因及解决办法

我在测试Java的wait()和notify()方法时发现,将notify()放在deposit方法中时可以正常唤醒等待的线程,但把notify()放到Thread 2的run方法里(无论调用deposit方法前后)都无法生效,程序会进入无限等待状态。

原代码:

public class NewProgram {
static int balance = 0;

public void withdraw(int amount) {
    synchronized(this)
    {
        if(balance== 0) {
        try{
            System.out.println("Waiting for deposit");
            wait();
        } catch(InterruptedException e) {
            e.printStackTrace();
        }
        }
    }
        balance = balance - amount;
        System.out.println("Amount Withdrawn Successfully");   
}
public void deposit(int amount) {

    balance = balance + amount;
    System.out.println("Money Deposited Successfully");

    //  The Notify method here works
    //    synchronized(this) 
    //         {
    //             notify();
    //         }}

public static void main(String[] args) {

    NewProgram app = new NewProgram();
    Thread thread1 = new Thread (new Runnable(){

        @Override
        public void run(){
            app.withdraw(2000);
        }
    }); 
    
    thread1.start();

    Thread thread2 = new Thread(new Runnable(){

        @Override
        public void run() {
            try {
                Thread.sleep(5000);

            //  The Notify method here doesn't work
            //     synchronized(this) {
            //     notify();
            // }
                
            } catch (InterruptedException e) {
                
                e.printStackTrace();
            }
            
            app.deposit(3000);


            // The notify method from here doesn't work
            //synchronized(this) {
            //     notify();
            //}            
            System.out.println("Thread 2 ends");
        } 
    });
    thread2.start();
}}

问题根源

核心问题是锁对象不匹配:

  • withdraw方法中的wait()是在app(NewProgram类的实例)对象的同步块中调用的,线程1持有app对象的锁,并在该对象上进入等待状态。
  • 而run方法里synchronized(this)中的this,指向的是当前Runnable匿名内部类的实例,并非app对象。在这个无关的Runnable实例上调用notify(),根本无法唤醒在app对象上等待的线程。

解决办法

要唤醒在app对象上等待的线程,必须在app对象的同步块中调用notify()。修改Thread 2的run方法如下:

Thread thread2 = new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        app.deposit(3000);

        // 锁定app对象,在该对象上调用notify()
        synchronized(app) {
            app.notify();
        }            
        System.out.println("Thread 2 ends");
    } 
});

额外注意事项

  1. 防止虚假唤醒:建议用while循环替代if判断等待条件,因为线程可能被虚假唤醒,需要重新检查条件是否满足:
public void withdraw(int amount) {
    synchronized(this) {
        // 用while循环重复检查余额
        while(balance == 0) {
            try{
                System.out.println("Waiting for deposit");
                wait();
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    balance = balance - amount;
    System.out.println("Amount Withdrawn Successfully");   
}
  1. wait()、notify()/notifyAll()必须在持有对应对象锁的同步块/方法中调用,且操作的必须是同一个对象,否则无法正常实现线程间通信。

内容的提问来源于stack exchange,提问作者Anonymous_1354

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 07:06:18