#java #multithreading #concurrency
Вопрос:
Я самостоятельно изучаю многопоточность и параллелизм на Java. Пожалуйста, помогите мне понять этот фрагмент кода. Я создаю поток с логической переменной «стоп», метод «выполнить» непрерывно повторяется до тех пор, пока основной поток не установит переменную «стоп» в значение true после двухсекундного сна. Однако я наблюдаю, что этот код выполняется в бесконечном цикле. Что я здесь делаю не так?
public class Driver {
public static void main(String[] args) {
ThreadWithStop threadWithStop = new ThreadWithStop();
Thread thread = new Thread(threadWithStop);
thread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadWithStop.stopThread();
}
}
class ThreadWithStop implements Runnable {
private boolean stop;
public ThreadWithStop() {
this.stop = false;
}
public void stopThread() {
this.stop = true;
}
public boolean shouldRun() {
return !this.stop;
}
@Override
public void run() {
long count = 0L;
while (shouldRun()) {
count ;
}
System.out.println(count "Done!");
}
}
Комментарии:
1. Этот код работает нормально. Для меня это не зацикливается навсегда!
2. @Kris , к сожалению, то, что он нормально работает в вашей настройке, не означает, что код в порядке. Вот почему параллелизм-сложная тема. Здесь явно существует проблема видимости изменения логического флага. Удивительно, что такой простой пример должен подчеркнуть проблему, но из спецификации языка совершенно ясно, что в отсутствие какой-либо синхронизации мутация логического значения может быть не видна никакому другому потоку, кроме того, который выполняет изменение.
3. @GPI Я согласен, но видите ли вы какую-либо другую тему в этом контексте, вносящую изменения ?
4. Когда я читаю вопрос, «основной» поток выполняет мутацию, вторичный поток «ThreadWithStop» «наблюдает» за измененным логическим значением из другого потока.
5. Разве изменчивая переменная
stop
не должна решить проблему?
Ответ №1:
Ну, это не гарантировано остановится, но может случиться. Изменение, которое вы внесли в stop
, вызвав stopThread()
из основного потока, не гарантируется, что оно будет видно до тех ThreadWithStop
пор, пока вы каким-то образом не синхронизируетесь с ним.
Одним из способов достижения этой цели было бы защитить доступ к переменной с synchronized
помощью ключевого слова — см., например, официальный учебник Oracle по синхронизированным методам:
При следующих изменениях изменение в stop
гарантированно будет видимым.
class ThreadWithStop implements Runnable {
private boolean stop;
public ThreadWithStop() {
this.stop = false;
}
public synchronized void stopThread() {
this.stop = true;
}
public synchronized boolean shouldRun() {
return !this.stop;
}
@Override
public void run() {
long count = 0L;
while (shouldRun()) {
count ;
}
System.out.println(count "Done!");
}
}