当前位置: 首页 > 知识库问答 >
问题:

Java等待/通知-不唤醒线程

傅志诚
2023-03-14

我想做一个小练习来习惯等待/通知。我想做的是简单地启动一个线程,然后用等待让它进入睡眠状态,用通知唤醒它,多次。

我的代码是:

public class Simple{
    static final Thread mainThread = Thread.currentThread();

    public static void main(String[] args) throws InterruptedException {
        PrintThread printer = new PrintThread(0);
        printer.start();

        synchronized (mainThread){
            System.out.println("main sleeping while waiting for printer to be started");
            mainThread.wait();
            System.out.println("main woke up");


            for (int i = 0; i < 1000; i++) {
                synchronized (printer){
                    System.out.println("added num "+i);
                    printer.numToPrint = i;
                    System.out.println("main waking up printer");
                    printer.notifyAll();
                    System.out.println("main sleeping");
                    mainThread.wait();
                    System.out.println("main woke up");
                }
            }

        }

    }
}

class PrintThread extends Thread{
    public int numToPrint = -1;

    public PrintThread(int numToPrint){
        this.numToPrint = numToPrint;
    }

    @Override
    public synchronized void run() {
        System.out.println("printer started");
        while (true){
            try {
                synchronized (Simple.mainThread){
                    System.out.println("printer waking up main");
                    Simple.mainThread.notifyAll();
                }
                System.out.println("printer sleeping");
                wait();
                System.out.println("printer woke up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("printing num "+numToPrint);
        }
    }

}

我希望这会是这样

main sleeping while waiting for printer to be started
printer started
printer waking up main
printer sleeping
main woke up
added num 0
main waking up printer
main sleeping
printer woke up
printing num 0
printer waking up main
printer sleeping
main woke up
added num 1
...

相反,这样做:

main sleeping while waiting for printer to be started
printer started
printer waking up main
printer sleeping
main woke up
added num 0
main waking up printer
main sleeping

所以。。。通知似乎没有唤醒打印机线程?

这不应该是一个死锁,因为通过等待,我释放了所有的锁,所以主服务器不应该有任何对打印机的锁,打印机应该能够唤醒并打印。

我做错了什么?

共有2个答案

鞠凌龙
2023-03-14

属性:调用等待()释放锁(它正在监控的锁)并进入等待状态。它在同一对象上等待通知()或通知所有()。一旦通知()或通知所有()并在CPU中进行调度,它就会在恢复之前再次获取锁。

当你第一次在main方法中“同步(mainThread)”时,它基本上锁定了“mainThread”类对象。当调用 mainThread.wait() 时,mainThread 进入等待状态(等待某人在 mainThread 类对象上调用 notify 或 notifyAll)。

此时,PrintThread可能会占用CPU。这是当“synchronized(Simple.mainThread)”被调度并锁定“Simple.minThread”并通知所有等待“Simple.mainThread”的线程时。在这个块完成后,PrintThread释放“Simple.mainThread”锁。

此时,主线程将再次尝试获取“主线程”上的锁,然后从调用等待的位置恢复。由于此时未获取“mainThread”锁,所以主线程获取锁并打印“main waked up”。

现在,这里遇到了for循环。记住:这里已经获取了“mainThread”类Object的锁。

现在在for循环内部,它获取“打印机”对象的锁。做一些计算,调用“printer.notify所有()”,所有等待“打印机”对象的线程都将被通知。

**这里要记住的一点是:由于代码光标仍在“同步(打印机)”内,因此“打印机”对象的锁定尚未释放。**

继续前进,打印“main sleeping”,然后调用“mainThread.wait()”。这尝试在已经获取的“mainThread”上获取锁(上面提到“记住:”在块中)并卡住,因为此后没有线程在“mainThread”上通知,并且“同步(打印机)”块永远不会结束,即即使在调用 NotifyAll() 之后,“打印机”对象的锁定也永远不会释放。

尝试在开始时的 Main 方法中添加以下代码,以测试上述场景。

synchronized (mainThread) {
            synchronized (printer){
                System.out.println("Before");
                mainThread.wait();
                System.out.println("After");
            }

解决方案:关闭“printer.notifyAll()”之后的“synchronized(printer)”块,以便在通知之后和获取“mainThread”之前释放“printer”锁。

南门朗
2023-03-14

很可能您的 notifyAll() 调用是在打印调用 wait() 再次之前调用的。问题在于您依赖于等待和通知所有调用都按照您希望的顺序发生。这是两个不同的执行线程,所以当然不能保证这一点,因此你得到了你所拥有的。

实现此目的的更好方法是创建一个公共的第三个共享对象,两个线程都可以获取锁定。这将在两个线程等待访问此对象时同步它们。

另外,您应该阅读 Javadocs for Thread.wait、notify和 notifyAll。如果你这样做了,你会发现你永远不应该在线程上调用这些方法,因为它们用于执行thread.join(不仅如此,我的“名声”是我相信这是我多年前的错误请求,当这不在JavaDoc中时,导致它被添加到Javadoc中。可能是其他人,但它发生在我要求之后:))

 类似资料:
  • 本文向大家介绍Java多线程基础 线程的等待与唤醒(wait、notify、notifyAll),包括了Java多线程基础 线程的等待与唤醒(wait、notify、notifyAll)的使用技巧和注意事项,需要的朋友参考一下 本篇我们来研究一下 wait() notify() notifyAll() 。 DEMO1: wait() 与 notify() DEMO1 输出: 注意: 使用 wait

  • 问题内容: 想象一下,您在Java中有一个典型的生产者- 消费者模式。为了提高效率,您要使用而不是在将新元素添加到队列时使用。如果两个生产者线程调用notify,是否保证将唤醒两个不同的正在等待的使用者线程?还是彼此之间很快触发的两个s导致同一用户线程排队两次唤醒?我找不到该部分描述此工作原理的API。Java是否有一些原子内部操作可仅一次唤醒线程? 如果仅一个消费者正在等待,则第二个通知将丢失,

  • 我正在创建一个简单的web代理应用程序使用Java。基本上,main方法创建一个RequestReceiver对象,该对象具有侦听web浏览器http请求的ServerSocket。从ServerSocket.Accept()返回的套接字创建一个新的连接对象,并将其放入线程池中。

  • 本文向大家介绍Java等待唤醒机制原理实例解析,包括了Java等待唤醒机制原理实例解析的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了Java等待唤醒机制原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 线程的状态 首先了解一下什么是线程的状态,线程状态就是当线程被创建(new),并且启动(start)后,它不是一启动就进入

  • 问题内容: 我写了一个永远不会停止的测试应用程序。它发出(是一个对象),但是我从不打电话通知。为什么此代码结束?尽管主线程在上同步,但生成的线程仍在运行,因此不会锁定该对象。 结果是主线程等待5秒钟,在此期间工作人员提供其输出。然后,在5秒钟后,程序退出。不等。如果主线程在5秒钟内没有进入睡眠状态(对此行进行了注释),则实际上将等到工作人员完成操作。当然,这里是一种使用方法,但是,出乎意料的是,它

  • 我有一段代码 如您所见,我首先将标志设置为false,这样其中一个线程就可以进入Sum2Elements方法并将其更改为true,从而让所有人都等待。 我知道在同步代码中,只有一个线程可以完成它的任务,这里我有两个同步方法,这是否意味着两个线程在每次通知之后都在尝试执行这个方法? 如果是这样,那么一个线程是否不可能输入Sum2Elements,在另一个线程进入InsertElement之前将标志更