Вот версия примера программы, измененная для введения цикла, который проверяет переменную условия. Таким образом, можно избежать плохих предположений о положении вещей после того, как нить повторных получает блокировку после пробуждения от ожидания, и нет никакого порядка зависимости между двумя потоками:
public class W extends Thread {
long sum;
boolean done;
public static void main(String[] args) throws InterruptedException {
W w = new W();
w.start();
synchronized(w) {
while (!w.done) {
w.wait();
}
// move to within synchronized block so sum
// updated value is required to be visible
System.out.println(w.sum);
}
}
@Override public synchronized void run() {
for (int i = 0; i < 1000000; i++) {
sum += i;
}
done = true;
// no notify required here, see nitpick at end
}
}
Это не достаточно, чтобы ждать уведомления , потому что вы указываете (зависимость порядка, где вы полагаетесь на условие гонки, надеясь, что один поток приобретает монитор перед другим), а также по другим причинам. Во-первых, поток может проснуться от ожидания, не получив уведомления, вы не можете предположить, что вообще был вызван запрос.
Когда поток ждет, он должен сделать это в цикле, где в тесте на цикле он проверяет некоторые условия. Другой поток должен установить эту переменную условия, чтобы первый поток мог ее проверить. Рекомендация, которую составляют the Oracle tutorial, составляет:
Примечание: всегда вызывайте ожидание внутри цикла, который проверяет состояние ожидания. Не предполагайте, что прерывание было для конкретного условия, которого вы ожидали, или что условие все еще верно.
Другие nitpicks:
Как ваш пример написано, виртуальная машина не требуется внести изменения в сумму переменной видна в основной поток. Если вы добавите метод синхронизированного экземпляра для доступа к переменной суммы или получите доступ к сумме в синхронизированном блоке, то основному потоку будет гарантировано увидеть обновленное значение суммы.
Глядя на ваше ведение журнала, нет ничего серьезного в прерванном исключении, это не значит, что все пошло не так. Прерывание прерывания возникает, когда вы вызываете прерывание по потоку, устанавливаете его флаг прерывания, и этот поток либо находится в ожидании, либо спящий, либо вводит метод ожидания или сна с установленным флагом. В моем примере игрушек в верхней части этого ответа я помещал исключение в предложение throws, потому что знаю, что этого не произойдет.
Когда поток завершается, он уведомляет об этом все, что ожидает что-либо, ожидающее этого объекта (опять-таки, это будет реализовано объединение). Стиль лучше использовать Runnable вместо Thread, отчасти из-за этого.
В этом конкретном примере было бы разумнее назвать Thread # join на суммирующем потоке, а не вызвать wait.
Вот пример переписан использовать объединение вместо:
public class J extends Thread {
private long sum;
synchronized long getSum() {return sum;}
public static void main(String[] args) throws InterruptedException {
J j = new J();
j.start();
j.join();
System.out.println(j.getSum());
}
@Override public synchronized void run() {
for (int i = 0; i < 1000000; i++) {
sum += i;
}
}
}
темы # Объединить ждать, блокировка на объекте потока. Когда суммарный поток завершается, он отправляет уведомление и устанавливает его флаг isAlive равным false. Между тем в методе соединения основной поток ожидает объекта суммирующего потока, он получает уведомление, проверяет флаг isAlive и понимает, что ему больше не нужно ждать, поэтому он может оставить метод соединения и распечатать результат.
Отойдите от этих базовых API до расширенных API, таких как java.util.concurrent.Executors и ExecutorService.Посмотрите на образец кода: http://examples.javacodegeeks.com/core-java/util/concurrent/executorservice/java-executorservice-example-tutorial/ –
@ sunrise76, классы в 'java.util.concurrent' не являются" более продвинутые: «Они работают на более высоких уровнях абстракции. В коде для написания кода devloper обязательно должны использоваться инструменты более высокого уровня, но _student_ будет хорошо понимать примитивы, на которых создаются эти инструменты более высокого уровня. Так же, как люди, которые изучили ассемблер, принимают более обоснованные решения, когда они пишут на языке более высокого уровня, поэтому люди, которые узнали о мьютексах и переменных условий и атомных операциях, лучше используют очереди и пулы потоков и т. Д. –
Ваше понимание верно. 'foo.notify()' ничего не делает, если ни один другой поток не блокируется в вызове 'foo.wait()' в тот же момент. –