【问题标题】:How does notify work in Java threads?通知如何在 Java 线程中工作?
【发布时间】:2013-08-26 13:31:00
【问题描述】:

我不熟悉在 java 中使用线程。我有一个简单的读写器问题,当作者进入线程时,读者将等待作者完成。

但是,当我运行我的程序时,我发现我的线程没有得到通知?这是为什么呢?

我的代码如下:

public class ReaderWriter  {

Object o = new Object();
volatile boolean writing;
Thread readerThread = new Thread( "reader") {

    public void run() {

        while(true) {
            System.out.println("reader starts");
            if(writing) {
            synchronized (o) {
                try {
                    o.wait();
                    System.out.println("Awaked from wait");
                } catch (InterruptedException e) {                      
                    e.printStackTrace();
                }

            }
        }

            System.out.println( "reader thread working "+o.hashCode());

        }

    }
};
Thread writerThread = new Thread("writer" ) {
    public void run() {
        System.out.println( " writer thread");
        try {
            synchronized (o) {
                writing = true;
                System.out.println("writer is working ..  ");
                Thread.sleep(10000);                    
                writing = false;
                o.notify();
                System.out.println("reader is notified");
            }


        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};

public static void main(String[] args) {
    ReaderWriter rw=new ReaderWriter();
    rw.readerThread.start();

    rw.writerThread.start();

}

}

【问题讨论】:

  • 你得到的输出是什么?您期望的输出是什么?
  • @BrentWorden 我希望读者“从等待中醒来”,但程序不会终止
  • @Achyut 我也有同样的问题。我放弃并使用了 notifyAll();这不是答案,而是一种解决方法
  • @BrentWorden 在我的情况下 notifyAll 也不起作用
  • 好吧,reader-thread 得到了通知,但是程序并没有终止,因为 reader-thread 是一个无限循环。

标签: java multithreading wait notify


【解决方案1】:

问题出在synchronized (o) 函数上。

同步函数使线程同步并在那里通过 它一次只对对象o 执行一个线程。因此 而writing 的值为真。它不允许第二胎面 readerThread 执行由于synchronized (o) 中的readerThread

你得到一个无限循环,因为没有终止语句。
查看here 以了解线程何时终止

看这段代码了解更多同步函数

synchronized(object) {
   // statements to be synchronized
}

这里,object 是对正在同步的对象的引用。一个 同步块确保调用属于 对象仅在当前线程成功进入后出现 对象的监视器

.

阅读本文以了解通知方法

JavaSW 中的 Object 类具有三个 final 方法,允许线程就资源的锁定状态进行通信。这些方法是wait()、notify() 和notifyAll()。线程通过具有该资源实例的同步块获得特定资源的锁定。假设一个线程要求另一个线程在对资源进行操作之前对资源执行某种操作。该线程可以在资源上同步并在资源上调用 wait() 方法。 这表示线程将等待,直到收到可以继续执行的通知。

wait() 方法可以将可选的超时值作为参数。 如果使用此值,则意味着线程将等待直到 它被通知或者一旦超时值它将继续执行 已经过去了。

如果一个线程需要在另一个线程对该资源进行操作之前对某个资源执行任务(并且另一个线程正在通过该资源上的wait() 方法等待),则该线程需要对该资源进行同步。它可以对资源执行其操作。

为了在这些操作完成后通知等待线程,调用资源上的notify() 方法。这通知等待线程它可以继续执行操作。如果多个线程正在等待资源,则无法保证哪个线程将获得对资源的访问权。如果希望唤醒所有等待的线程,可以在资源上调用notifyAll() 方法。

【讨论】:

  • @Achyut 问题得到解答了吗??
  • 感谢您的回复。我得到了关于通知如何工作的部分答案?但实际上不是我的问题的答案。 syncronized(o) 实际上并没有造成问题,因为它是由 reader 和 writer 获取和释放的监视器。
  • @Achyut syncronized(o) 在其括号内阻止代码的执行(这就是为什么你没有从等待消息中唤醒)。因此它回到下一个线程。!即作家线程。并且编写器线程将再次通知读者,但由于同步(o)而不会执行。这个过程将继续(发出无限语句)。它只会在你给出终止语句之前终止。!!
  • @achyut 这不是它的工作方式,我们帮助您消除疑虑。你重做你的代码并验证它是否有效。解决问题后,您可以接受帮助您解决问题的答案.. !!查看meta.stackoverflow.com了解更多详情
  • 是的,我重做我的代码并在这个论坛上提问只是因为我没有得到预期的输出。对此没有同步(o)负责,但罪魁祸首是我在同步块中提到的 write = true 和 writing = false。
【解决方案2】:

问题是writing 设置为错误的事先通知。

Thread.sleep() 单独不会唤醒其他等待线程。

【讨论】:

  • 谢谢,我注释掉了 write = false 但问题仍然存在
  • 我在 notify() 之后写了 writing=false 但还是一样。 Thread.sleep 仅用于编写器线程休眠一段时间。
【解决方案3】:

writing 设置为true 时,编写器线程 始终持有监视器对象上的锁。这就是为什么当writing 设置为true 时,您的阅读器线程 中的同步块永远不会被输入。

synchronized (o) { // Thread aquires lock on monitor o. writing is false.
    writing = true; // writing is true, lock still held.
    System.out.println("Writer is working...");
    Thread.sleep(1000); // Thread sleeps while holding lock.
    writing = false; // writing is changed back to false, lock still held.
    o.notify();
    System.out.println("Reader is notified");
} // Here lock is released, writing is false.

writing在获取锁之前设置为false,在释放锁时设置为false。当锁被持有时,阅读器线程中的这个同步块将不会被输入:

while (true) {
    if (writing) {
        synchronized (o) { // Lock always held by writer when writing=true.
            try {
                o.wait();
                System.out.println("Awaked from wait");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

【讨论】:

  • 感谢漂亮的描述,但根据情况是正确的,因为在获取锁之前没有写入,所以写入= false,释放锁后写入必须设置为false,因为现在写入已经完成。跨度>
  • yes when writing get false 监视器被释放,因此应该打印语句“reader thread working "+o.hashCode()”
【解决方案4】:

感谢您的合作和宝贵的建议,我对我的代码做了一点改动

public class ReaderWriter  {

Object o = new Object();
volatile boolean writing;
Thread readerThread = new Thread( "reader")  {

    public void run() {
        System.out.println("reader starts");

            synchronized (o) {
                    System.out.println("reader aquire the lock");
                    while(writing) {
                        try {                               
                            System.out.println("Reader goes to wait ....");
                            o.wait();                               
                            System.out.println("Awaked from wait");
                        } catch (InterruptedException e) {                      
                            e.printStackTrace();
                        }
                    }
                    while(!writing) {
                        //do something  
                        System.out.println("hiiiiiiii");
                    }
                    System.out.println("reader release the lock");  
                }
        }

    };

Thread writerThread = new Thread("writer" ) {
    public void run() {
        System.out.println( "writer starts");
        try {
            writing = true;
            synchronized (o) {                  
                System.out.println("writer aquire the lock");
                Thread.sleep(10000);        
                o.notify();                 
                System.out.println("reader is notified");
                System.out.println("writer release the lock");
            }
            writing = false;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};

public static void main(String[] args) {
    ReaderWriter rw=new ReaderWriter();
    rw.readerThread.start();        
    rw.writerThread.start();

}

}

现在我发现了一件非常有趣的事情,即“等待等待”只有当读者首先获得锁并且在更正之后我将 write = true 和 writing = false 放在同步块之外,以便在读者获得锁之后writer 来了,它改变了写入标志,因此 reader 去等待并为 writer 释放锁, writer 获取锁执行 Thread.sleep 并通知 reader,从而为 reader 释放锁。 Reader 醒来,发现 writer 将写入标志设置为 false,于是 reader 开始执行其操作。

【讨论】:

【解决方案5】:
There can be only two cases when writer thread first enters its critical section or when reader thread enters first since by no means you are stopping one to sleep for other to start first.

1) In first case when writer thread enters critical section first synchronized block locks the object o and after waking up from sleep you notify but you are not releasing lock explicitly till sync block ends which unlocks the object automatically .So even if notification is sent inside block reader thread wont be able to take the lock .So after syn block of your writer thread is over you reader thread would run uninterrupted assuming there is no interrupt from third thread .

2) In second case when  you reader thread is scheduled by scheduler to run first it will obtain a lock and on finding flag false it will enter infinite loop of printing hi on output console and its is infinite since you have take lock of object and entered infinite loop which makes writer thread wait indefinitely on reader thread  

Correcting your code as below for reader thread 

synchronized (o) {
    System.out.println("reader aquire the lock");
    if(writing) {
        try {                               
            System.out.println("Reader goes to wait ....");
                            System.out.println("Awaked from wait");
        } catch (InterruptedException e) {                      
            e.printStackTrace();
        }
    }
    else{
        //do something 
        o.notify(); 
        o.wait(); // in this case only reader release the lock would only be printed and if block wont be executed
    }
    System.out.printn("reader release the lock");  
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 2020-05-18
    • 1970-01-01
    • 2020-05-01
    • 1970-01-01
    • 2015-08-30
    • 2018-08-29
    相关资源
    最近更新 更多