【问题标题】:notify() and notifyAll() doesn't work in my java codenotify() 和 notifyAll() 在我的 java 代码中不起作用
【发布时间】:2016-12-26 14:48:10
【问题描述】:

所以我一直在用 Java 编写一个简单的等待/通知示例,但由于某种原因,我无法让它正常运行。如果有人能够看到可能是什么问题,将不胜感激!

class producer implements Runnable {
    StringBuffer sb;
    producer() {
        sb=new StringBuffer("");
    }

    public void run () {
        synchronized(sb) {
            for(int i = 0; i < 10; i++) {
                try { 
                    sb.append(i+" ");
                    System.out.println("Appending ... "); 
                } catch (Exception e) {}
            }
            sb.notify(); 
        }
    }
}

class consumer implements Runnable {
    producer p;
    consumer(producer pp) {
        this.p = pp;
    }

    public void run() {
        System.out.println("Rreached");
        synchronized(p.sb) { 
            try { 
                 p.sb.wait(); 
            } catch (Exception e) {}
            System.out.println(p.sb);
        }
    }
}

class Thread_Comunication {
    public static void main (String [] args) {
        producer p = new producer();
        consumer c = new consumer(p);

        Thread t1 = new Thread(p);
        Thread t2 = new Thread(c);
        t1.start();
        t2.start();
    }
}

输出:

Appending ...
Rreached   // randome Position
Appending ...
Appending ...
Appending ...
Appending ...
Appending ...
Appending ...
Appending ...
Appending ...
Appending ...  

所以由于某种原因线程 t1 没有唤醒 t2 还是我完全错过了其他东西?

【问题讨论】:

  • 至少将e.printStackTrace(); 放入您的catch 块中,否则您永远不会知道是否发生了异常。
  • 注意:Java 语言约定类名以大写名称开头(因此,producer 不是一个好的类名)。类、方法或包名称中不使用下划线,仅在全大写的常量名称中使用下划线。类的名称类似于 ThreadCommunication,而不是 Tread_Comunication。还建议给变量起有意义的名称,而不是 'p' 和 'pp'。
  • 关闭投票者:发布的代码确实重现了问题(至少尽可能多地重现,因为代码取决于调度程序可能会或可能不会发生的竞争条件)。在发布的代码和显示的输出之间,弄清楚这里发生了什么是切实可行的。

标签: java multithreading wait synchronized notify


【解决方案1】:

Notify 不会做任何事情,除非另一个线程正在等待。您的代码完全依赖于通知(它需要条件变量的地方),并且依赖于在生产者之前运行的消费者才能使其工作。

根据您的输出,生产者首先运行;它将在消费者有机会运行之前全部执行。 (为了让消费者运行,它需要获取生产者持有的 sb 的锁。)生产者调用 notify,但没有线程在等待,所以它没有效果。然后消费者等待并且没有通知来,所以它无限期地挂起。

如果消费者先运行,则代码将正常终止。

避免编写依赖于一个线程恰好在另一个线程之前运行的代码,因为您无法控制首先执行的内容。当您等待时,您需要在测试条件的循环内进行。造成这种情况的多种原因之一是,如果在线程开始等待之前设置了条件,则线程可以知道不等待。

更改代码以使用条件:

import java.io.*;

class producer implements Runnable {
    StringBuffer sb;
    boolean done = false;
    producer() {
        sb=new StringBuffer("");
    }

    public void run () {

        synchronized(sb) {

            for(int i=0;i<10;i++) {
                try { 
                    sb.append(i+" ");
                    System.out.println("Appending ... "); 
                } catch (Exception e) {}
            }
            sb.notify(); 
            done = true;
        }


    }
}

class consumer implements Runnable {
    producer p;
    consumer(producer pp) {
        this.p=pp;
    }

    public void run() {
        System.out.println("Rreached");
        synchronized(p.sb) { 
            try { 
                while (!p.done) {
                 p.sb.wait(); 
                }
            } catch (Exception e) {}

            System.out.println(p.sb);
        }
    }
}


public class Communication {
    public static void main (String [] args) throws Exception {
        producer p= new producer();
        consumer c= new consumer(p);

        Thread t1= new Thread(p);
        Thread t2= new Thread(c);
        t2.start();
        t1.start();
    }
}

【讨论】:

    【解决方案2】:

    所以我一直在用 Java 编写一个简单的等待/通知示例,但由于某种原因,我无法让它正常运行。

    您的代码的问题是notify() 没有持久化。如果producer 进入synchronizedfirst,那么consumer 将无法进入它并到达wait,直到producer 退出循环并完成。由于所有notify 调用都发生在synchronized 块的内部,当consumer 到达wait() 时,producer 已经完成并且不再调用@ 987654332@。这意味着consumer 已挂起。

    即使您首先启动consumer,您仍然有可能导致producer 首先进入其synchronized 块的竞争条件——尤其如此,因为consumer 调用@987654338 @这需要时间。虽然不是“修复”,但如果您在调用synchronized 之前使用Thread.sleep(100) 减慢producer,您应该会看到它现在可以工作,因为consumer 到达它的wait() before producer 将其锁定。

    有几种方法可以让您正确解决此问题。通常我们使用等待/通知 consumer 应该检查的另一个变量。在您的情况下,这可能是 sb 变量本身,因为它受到保护。所以consumer 可以这样做:

    synchronized (p.sb) { 
        try {
            // do we need to wait to see if the producer added stuff?
            if (p.sb.length() == 0) {
                p.sb.wait();
            }
        } catch (InterruptedException e) {
            // this is always a good pattern to preserve the interrupt flag
            Thread.currentThread().interrupt();
            return;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-21
      • 2011-04-17
      • 1970-01-01
      • 1970-01-01
      • 2011-02-19
      相关资源
      最近更新 更多