【问题标题】:Is a notify signalled on thread finish? Why does this code sample work?线程完成时是否发出通知?为什么此代码示例有效?
【发布时间】:2012-07-21 12:14:23
【问题描述】:

我正在寻找线程的一些谜题,但我无法弄清楚为什么以下内容始终打印999999

class Job extends Thread {  
    private Integer number = 0;  
    public void run() {  
        for (int i = 1; i < 1000000; i++) {  
            number++;  
        }  
    }  
    public Integer getNumber() {  
        return number;  
    }  
}  
public class Test {  
    public static void main(String[] args)   
    throws InterruptedException {  
        Job thread = new Job();  
        thread.start(); 
        synchronized (thread) {  
            thread.wait();  
        }  
        System.out.println(thread.getNumber());  
    }  
}   

同一个锁上没有notify(并且虚假唤醒似乎被忽略了)。
如果线程完成,是否会发出通知或其他信号?
为什么main 打印结果而不是“卡住”等待?

【问题讨论】:

    标签: java multithreading concurrency wait notify


    【解决方案1】:

    为澄清起见,我已将您的代码修改为:

    Job thread = new Job();
    thread.start();
    final Object lock = new Object();
    synchronized (lock) { lock.wait(); }
    System.out.println(thread.getNumber());
    

    现在它阻塞了。这是对@Nitram 在他的回答中解释的内容的第一手确认。如果您在Thread 实现代码处care to have a look,很明显为什么这是观察到的行为。

    【讨论】:

    • 这样就相当于一个notify?但是Job没有持有任何锁
    • While you are waiting, the lock on that monitor is realeased, anyway我不明白你的意思
    • 我认为这不能回答问题。为什么第二个线程终止时第一个线程从wait返回?
    • @MarkoTopolnik:是的,但是Job 在哪里持有任何锁?不在可见代码中。
    • “这是第一手确认” - OP首先确认了它。问题是“为什么”,仍然没有答案。
    【解决方案2】:

    嗯....notify 用于提前通知等待锁定对象的线程。如果您不使用 Notify ,那么当它完成时肯定会释放锁定。

    So that is equivalent to notify
    

    不,不是……考虑下面的情况。

    class Job extends Thread {  
        private Integer number = 0;  
        public void run() {
    
           synchronized(this) {
           for (int i = 1; i < 1000000; i++) {  
                number++;  
            }  
            notify();     //releases lock here and your main thread continues
            do sumthing...
            do sumthing...
            }  
        }
        public Integer getNumber() {  
            return number;  
        }  
    }  
    

    如果你不使用 notify() ...只有在你做完所有事情后才会释放锁..

    【讨论】:

    • 嗯?! notify 不会释放任何锁。
    • 您的代码将在运行时抛出异常,因为您在没有锁的情况下调用notify
    【解决方案3】:

    线程结束时会自动通知线程对象,这就是主线程不会卡住的原因。

    【讨论】:

    • 有任何来源支持这个吗?我在 Javadocs 中没有找到任何关于此的内容。
    【解决方案4】:

    一旦线程执行结束,只需放置一个Thread 通知所有等待线程。它不是建议的为什么要这样做,但它有效。要在线程结束时同步,请使用Thread.join

    【讨论】:

    • but it works 你的意思是你没有这方面的文档,但这是你观察到的一种行为?
    • 是的,它可以工作,因为垂死的线程将释放使用Object.wait 应用的锁。但是如果你使用 notify,wait 也会被释放。如果您想确保在线程结束时同步,您必须使用Thread.join
    • 问题不在于如何以及是否释放锁。问题在于如何以及是否会向对象发出信号,从而导致wait 返回。
    • Nitram,这与释放任何锁无关,因为任何一个线程都没有持有任何锁。这是关于Thread using this for its own purposes,真是设计失败。
    【解决方案5】:

    注意:此答案已被广泛编辑。


    这种行为的原因是“某人”在内部调用notifyAll。这个“某人”就是 JVM 本身,您可以在此处的 C 源代码中“看到”:

    http://hg.openjdk.java.net/jdk7/hotspot/hotspot/file/f95d63e2154a/src/share/vm/runtime/thread.cpp

    在第 1531 行,方法 ensure_join 调用 notifyAll。这与 java.lang.Thread#join 中的 wait 调用对应(正如 Marko 和其他人所指出的那样)。

    ensure_join 在方法 JavaThread::exit 的第 1664 行依次调用。


    由于这是“内部簿记”,任何人都不应依赖这种行为。

    【讨论】:

    • 这是 Java 实现者从经验中学到的糟糕设计的一个例子——将可公开访问的对象的监视器用于内部目的。 See also the flip-side of your code paste.
    • notifyAll 在 threadGroup 的监视器上调用,而不是在线程上。
    • @tibtof:没错。对不起。
    【解决方案6】:

    在 Java 7 的 Javadoc 中 Thread.join(long)

    此实现使用以 this.isAlive 为条件的 this.wait 调用循环。当线程终止时,将调用 this.notifyAll 方法。建议应用程序不要在 Thread 实例上使用 wait、notify 或 notifyAll。

    以这种方式直接使用线程被认为是不实用的。注意:wait() 可能因多种原因而结束,可能是虚假的。


    基于与@Voo 评论相关的谜题。关键是你不应该玩弄 Thread 的内部行为,因为这更有可能导致混乱。

    public static String getName() {
        return "MyProgram";
    }
    public static void main(String... args) {
        new Thread() {
           public void run() {
               System.out.println("My program is " + getName());
            }
        }.start();
    }
    

    这个程序打印什么?

    【讨论】:

    • 有趣...这是最近添加的,它不在我的 Java 6 源代码中
    • 添加了一条来自 Java 7 的注释。AFAIK,所有以前的版本都这样做了。
    • 是的,这已经是 Java 解谜游戏中的一个谜题,所以它至少从 Java5 开始就存在了。真的只是永远不要创建匿名线程并避免这些问题。
    • @Voo 在这个话题上添加了一个谜题。 ;)
    • @PeterLawrey:我认为那个谜题对这个问题没有帮助。问题不在于模棱两可的方法名称,而在于谁以秘密方式调用notifyAll
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-25
    • 1970-01-01
    • 1970-01-01
    • 2017-07-18
    • 2014-10-25
    • 2013-09-24
    相关资源
    最近更新 更多