Уведомляет ли уведомление о завершении резьбы? Почему этот образец кода работает?

Я рассматриваю некоторые головоломки для потоков, и я не могу понять, почему следующее последовательно печатает 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 печатает результат и не получает "застревание" ожидания?

Ответы

Ответ 1

В Javadoc для Java 7 Thread.join(long)

В этой реализации используется цикл this.wait вызовов, обусловленных this.isAlive. Когда поток завершается, этот метод this.notifyAll вызывается. Рекомендуется, чтобы приложения не использовали wait, notify или notifyAll для экземпляров Thread.

Использование Thread напрямую таким образом считается плохим практическим. Примечание: 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();
}

Что печатает эта программа?

Ответ 2

Для уточнения, я изменил ваш код на это:

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

Теперь он блокируется. Это из первых рук подтверждение того, что @Nitram объяснил в своем ответе. Если вы обратите внимание на код реализации Thread, будет совершенно очевидно, почему это наблюдаемое поведение.

Ответ 3

ПРИМЕЧАНИЕ.. Этот ответ был широко отредактирован.


Причиной такого поведения является то, что "кто-то" вызывает notifyAll внутренне. Этот "кто-то" является самой JVM, так как вы можете "видеть" в источниках C здесь:

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

В строке 1531 метод ensure_join вызывает notifyAll. Это аналог вызовов wait в java.lang.Thread#join (как отмечено Марко и другими). ​​

ensure_join, в свою очередь, вызывается в строке 1664 в методе JavaThread::exit.


Поскольку это "внутренний бухгалтерский учет", никто не должен полагаться на это поведение.

Ответ 4

Проще говоря, Thread уведомляет все ожидающие потоки после завершения выполнения потоков. Это не предложение, почему это нужно, но оно работает. Для синхронизации в конце потока скорее используйте Thread.join.

Ответ 5

Объект потока автоматически уведомляется, когда поток заканчивается, поэтому основной поток не застревает.

Ответ 6

Хорошо.... 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()... блокировка будет выпущена только после выполнения всех ваших сумм.