Почему в следующем коде Java:
public class WaitForOther {
private volatile boolean in = false;// need volatile for transitive closure
// to occur
public void stoppingViaMonitorWait() throws Exception {
final Object monitor = new Object();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (monitor) {
in = true;
try {
monitor.wait(); // this releases monitor
//Thread.sleep(8000); //No-op
System.out.println("Resumed in "
+ Thread.currentThread().getName());
} catch (InterruptedException ignore) {/**/
}
}
}
}).start();
System.out.println("Ready!");
while (!in); // spin lock / busy waiting
System.out.println("Set!");
synchronized (monitor) {
System.out.println("Go!");
monitor.notifyAll();
}
}
Снятие комментариев с Thread.sleep(8000); //No-op
приводит к сокращенному выводу:
Ready!
Set!
Go!
Который в противном случае правильно возобновляется в прерванном Thread-0:
Ready!
Set!
Go!
Resumed in Thread-0
Вот тест JUnit, который вызывает указанное выше поведение, как просили в комментариях:
public class WaitForOtherTest {
WaitForOther cut = new WaitForOther();
@Test
public void testStoppingViaMonitorWait() throws Exception {
cut.stoppingViaMonitorWait();
}
}
Благодарность!
2 ответа
Ваше наблюдение не связано с моделью памяти Java. Попробуйте запустить следующий код:
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Will not be printed when running JUnit");
}
}.start();
Если вы запустите его в основном методе, поток, не являющийся потоком демона, будет поддерживать процесс в рабочем состоянии до периода ожидания. закончился так, что печатается последняя строка, после чего процесс завершается.
Поскольку JUnit тестирует потенциально сломанный код, он должен предположить, что поток, запущенный из модульного теста, может никогда не закончиться. Таким образом, структура не будет ждать, пока все запущенные потоки завершатся, а явно завершит процесс Java после возврата всех тестовых методов. (Учитывая, что вы не установили тайм-аут для ваших тестов, к которому этот тайм-аут применяется дополнительно.) Таким образом, набор тестов JUnit не уязвим для неработающего кода при тестировании.
Однако, если выполнение вашего набора тестов занимает больше времени, чем время выполнения запущенного потока, вы все равно можете увидеть напечатанный оператор. Например, добавив еще один тест:
public class WaitForOtherTest {
WaitForOther cut = new WaitForOther();
@Test
public void testStoppingViaMonitorWait1() throws Exception {
cut.stoppingViaMonitorWait();
}
@Test
public void testStoppingViaMonitorWait2() throws Exception {
Thread.sleep(9000);
}
}
Вы все еще можете видеть напечатанную строку.
Однако обратите внимание, что этот результат печати является довольно нестабильным, поскольку он зависит от определенного, недетерминированного порядка выполнения теста и короткого времени выполнения для не ожидающего кода. (Тем не менее, это обычно работает для запуска этого примера, что достаточно хорошо для демонстрационных целей).
Этот код неправильно использует такие низкоуровневые конструкции взаимодействия потоков Java, как wait
и notify
. Непонятно, что вы хотите установить с помощью этого кода.
Вот мои наблюдения за поведением вашей программы (они отличаются от ваших. Я запускал ее как в среде IDE, так и в командной строке с использованием серверных и клиентских компиляторов):
С sleep()
// закомментированным //:
Ready!
thread is daemon? : false
Set!
Go!
Resumed in Thread-0
С sleep()
раскомментированным :
Ready!
thread is daemon? : false
Set!
Go!
<< 8 seconds pass >>
Resumed in Thread-0
Таким образом, его поведение соответствует вашему проекту: поток main
запускает поток, не являющийся демоном (скажем, thread-0
). Затем поток main
и thread-0
соревнуются за блокировку (monitor
), но thread-0
всегда побеждает, потому что вы этого хотите.
Если thread-0
захватывает блокировку, он устанавливает энергозависимую запись для in
и немедленно снимает блокировку для потока main
, чтобы уведомить его об этом wait()
. Теперь поток main
видит изменчивое обновление (гарантия видимости), выходит из режима ожидания-занятости, захватывает блокировку, печатает "Go" и уведомляет thread-0
, который, в зависимости от его состояния сна, получает уведомление.
Поток main
не выиграет гонку за блокировку из-за ожидания занятости.
Я добавил строку, чтобы уточнить, является ли ваш поток демоном или нет, и поскольку JVM не завершается, потому что ваш поток не является потоком демона, он остается достаточно долго, чтобы поток проснулся из сна и печатает свою строку.
Я добавил строку, чтобы уточнить, является ли ваш поток демоном или нет, и поскольку JVM не завершается, потому что ваш поток не является потоком демона, он остается достаточно долго, чтобы поток проснулся из сна и печатает свою строку.…
Похожие вопросы
Связанные вопросы
Новые вопросы
java
Java — это высокоуровневый объектно-ориентированный язык программирования. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег часто используется вместе с другими тегами для библиотек и/или фреймворков, используемых разработчиками Java.