为啥在我的 java 代码中没有调用通知

Posted

技术标签:

【中文标题】为啥在我的 java 代码中没有调用通知【英文标题】:why is notify not getting called in my java code为什么在我的 java 代码中没有调用通知 【发布时间】:2015-02-27 09:14:33 【问题描述】:

这是我的代码:

public class ProducerConsumer


    public static void main(String[] args)
    
        ProduceCosumeData p = new ProduceCosumeData();
        ProducerT pt= new ProducerT(p); // with p obj i am creating thread
        ConsumerT ct=new ConsumerT(p); // with same p obj i am creating thread
        pt.start();
        ct.start();  //i am starting 2 threads
    


class ProduceCosumeData

    boolean flag;
    public synchronized void printStringP(int n)
    
        for(int i=0;i<n;i++)
        
            try
                if(flag)   //for frist time flag is flase so, wait will skip
                    wait();
                else
                    flag=true;    //for next time onwards wait() will get call

                System.out.print("Pay");
                notify();//after this why my wait() not canceling in inprintStringC()
            catch(Exception e)
            
                System.out.print(e);
            
        
    
    public synchronized void printStringC(int n)
    
        for(int i=0;i<n;i++)
        
            try
                wait();   // why it is not out of this after notify()
                System.out.print("Tm");
                notify();
            catch(Exception e)
            
                System.out.print(e);
            
        
    


class ProducerT extends Thread

    ProduceCosumeData p;
    ProducerT(ProduceCosumeData p)
     
        this.p=p;   // i am saving the same obj for both threads
       
    public void run()
    
        p.printStringP(10); //it will print 10 times pay
    


class ConsumerT extends Thread

    ProduceCosumeData p;
    ConsumerT(ProduceCosumeData p)
     
        this.p=p;   // i am saving the same obj for both threads
    
    public void run()
    
        p.printStringC(10);   //it will print 10 times tm
    

我期待以下输出:

支付宝 支付宝 支付宝 ... 10 次

但我得到的输出是这样的:

支付..

接下来是漫长的等待。

以上两个函数在同一个对象中。 为什么 notify 没有释放 wait() 函数?即使我使用 notifyAll(),输出也保持不变。

【问题讨论】:

printStringP 可能会调用 notifyprintStringC 仍在等待进入该方法(因为它已同步)。 【参考方案1】:

在您的代码中,您的一个线程正在调用通知,而另一个线程仍未等待。这会产生一个死锁,两个线程都在等待。

您需要修复您对同步标志的使用,如果不需要,请不要调用等待。此外,在 wait() 之后检查锁定条件仍然可用是一个好习惯。

这是你的ProduceConsumeData 类,使用了固定的标志:

class ProduceCosumeData

    boolean flag;
    public synchronized void printStringP(int n)
    
        for(int i=0;i<n;i++)
        
            try
                while (flag == true)    
                    wait();
                
                flag=true;
                System.out.print("Pay");
                notify();
            catch(Exception e)
            
                System.out.print(e);
            
        
    
    public synchronized void printStringC(int n)
    
        for(int i=0;i<n;i++)
        
            try
                while(flag == false) 
                    wait();
                
                System.out.print("Tm");
                flag = false;
                notify();
            catch(Exception e)
            
                System.out.print(e);
            
        
    

【讨论】:

它工作正常。谢谢你。现在我学习了 2 个概念 1) 解除锁代码 2) 如何解决死锁 还有一个问题,为什么你使用 while (flag == true) wait();我们也可以使用 if(flag == true) wait(); 使用 if 有什么问题吗? @Yugandhar 您可以使用if 而不是while,但最好在wait() 之后再次检查条件。例如,如果修改了类以添加更多功能并且在其他地方使用了类的锁,那么您的同步将被破坏。如果您在wait() 之后再次检查条件,代码会更加健壮 @Yugandhar Deadlock 表示两个或多个线程,每个线程都在等待其他线程做某事。但是使您陷入僵局的问题称为丢失通知,在这种情况下您必须学会避免这种情况。 ***.com/questions/24655143/…【参考方案2】:

您在方法中使用了通用wait() 与同步。尝试使用对象同步的版本,例如synchronized(this) wait(); 等等,以防止多个线程对同一对象的循环依赖,这对于任何多线程程序来说都是非常危险的。

或者,更简单地说,在你的ProducerConsumerData 类中实现一个适当的clone() 方法,然后将这个对象传递给第一个线程,然后传递它的克隆。尝试在第二个线程的构造函数中使用p.clone() 而不是p

如上所述,您可以让 printStringP() 的 notify() 仅在 flag 为真时被调用,并非总是如此。

【讨论】:

据我所知,在这种情况下没有 diff b/w 同步方法或块【参考方案3】:

这是一个经典的误解,几乎所有尝试使用waitnotify 的人都会犯错。真的,他们太老了,太破了,恕我直言,他们甚至不应该被教导。

printStringP 调用notify() printStringC 还没有等待

class ProduceCosumeData 
    // Variable shared between threads should be volatile.
    volatile boolean flag;

    public synchronized void printStringP(int n) 
        for (int i = 0; i < n; i++) 
            try 
                //for frist time flag is flase so, wait will skip
                if (flag) 
                    System.err.println("Waiting in printStringP");
                    wait();
                 else 
                    System.err.println("flag now true");
                    flag = true;    //for next time onwards wait() will get call
                
                System.out.print("Pay");
                System.err.println("printStringP notify");
                notify();//after this why my wait() not canceling in inprintStringC()
             catch (Exception e) 
                System.out.print(e);
            
        
    

    public synchronized void printStringC(int n) 
        for (int i = 0; i < n; i++) 
            try 
                System.err.println("Waiting in printStringC");
                wait();   // why it is not out of this after notify()
                System.out.print("Tm");
                System.err.println("printStringC notify");
                notify();
             catch (Exception e) 
                System.out.print(e);
            
        
    


class ProducerT extends Thread 

    ProduceCosumeData p;

    ProducerT(ProduceCosumeData p) 
        this.p = p;   // i am saving the same obj for both threads
    

    public void run() 
        p.printStringP(10); //it will print 10 times pay
    


class ConsumerT extends Thread 

    ProduceCosumeData p;

    ConsumerT(ProduceCosumeData p) 
        this.p = p;   // i am saving the same obj for both threads
    

    public void run() 
        p.printStringC(10);   //it will print 10 times tm
    


public void test() 
    ProduceCosumeData p = new ProduceCosumeData();
    ProducerT pt = new ProducerT(p); // with p obj i am creating thread
    ConsumerT ct = new ConsumerT(p); // with same p obj i am creating thread
    pt.start();
    ct.start();  //i am starting 2 threads

打印

flag now true
PayprintStringP notify
Waiting in printStringP
Waiting in printStringC

要解决此问题不要使用等待/通知,除了非常有经验的人外,其他所有人都无法使用它。使用Locks 和Conditions 或几乎任何其他java.util.concurrent 类可以稳定地实现相同的功能。

【讨论】:

【参考方案4】:

printStringP 的第二次迭代中,flag 属性为true,然后两个线程正在等待。

【讨论】:

需要持有锁才能调用wait【参考方案5】:
Please find the below code snippet.

package com.java.examples;

public class ProducerConsumer 
    public static void main(String[] args) throws InterruptedException 
        ProduceCosumeData p = new ProduceCosumeData();
        ProducerT pt = new ProducerT(p); // with p obj i am creating thread
        ConsumerT ct = new ConsumerT(p); // with same p obj i am creating thread
        pt.start();
        Thread.sleep(1000);
        ct.start(); // i am starting 2 threads
    


class ProduceCosumeData 
    boolean flag = false;

    public synchronized void printStringP(int n) 
        for (int i = 0; i < n; i++) 
            try 
                if (flag) 
                    notify();
                 else
                    flag = true;
                System.out.println("Pay");
                if (i <= n - 1) 
                    wait();
                 else 
                    break;
                

             catch (Exception e) 
                System.out.print(e);
            
        
        notify();
    

    public synchronized void printStringC(int n) 
        for (int i = 0; i < n; i++) 
            try 
                if (flag) 
                    System.out.println("Tm");
                    if (i <= n - 1) 
                        notify();
                     else 
                        break;
                    
                 else
                    flag = false;
                wait();
             catch (Exception e) 
                System.out.print(e);
            
        
    


class ProducerT extends Thread 
    ProduceCosumeData p;

    ProducerT(ProduceCosumeData p) 
        this.p = p; // i am saving the same obj for both threads
    

    public void run() 
        p.printStringP(10); // it will print 10 times pay
    


class ConsumerT extends Thread 
    ProduceCosumeData p;

    ConsumerT(ProduceCosumeData p) 
        this.p = p; // i am saving the same obj for both threads
    

    public void run() 
        p.printStringC(10); // it will print 10 times tm
    

【讨论】:

以上是关于为啥在我的 java 代码中没有调用通知的主要内容,如果未能解决你的问题,请参考以下文章

为啥挂起的意图在从服务的通知中调用时不会启动活动

为啥 Galaxy S21 没有自定义通知声音

为啥我只能在我的 android 通知中按一次播放/暂停按钮?

为啥在 iOS 7 中我的本地通知没有显示横幅

在 Java 中,调用 PostgreSQL 函数,为啥我收到一个错误,通知该函数不存在?

为啥我的本地通知没有在 iOS 10 的前台触发?