Это своего рода Java Puzzler, который я наткнулся и не могу объяснить. Может, кто-нибудь может?Java Puzzler: занят ждать, когда потоки перестают работать
Следующая программа зависает через короткое время. Иногда после двух выходов, иногда после 80, но почти всегда до правильного завершения. Возможно, вам придется запустить его несколько раз, если это не произойдет в первый раз.
public class Main {
public static void main(String[] args) {
final WorkerThread[] threads = new WorkerThread[]{ new WorkerThread("Ping!"), new WorkerThread("Pong!") };
threads[0].start();
threads[1].start();
Runnable work = new Runnable() {
private int counter = 0;
public void run() {
System.out.println(counter + " : " + Thread.currentThread().getName());
threads[counter++ % 2].setWork(this);
if (counter == 100) {
System.exit(1);
}
}
};
work.run();
}
}
class WorkerThread extends Thread {
private Runnable workToDo;
public WorkerThread(String name) {
super(name);
}
@Override
public void run() {
while (true){
if (workToDo != null) {
workToDo.run();
workToDo = null;
}
}
}
public void setWork(Runnable newWork) {
this.workToDo = newWork;
}
}
Теперь ясно, что занятые петли ожидания - отличная идея в целом. Но это не об улучшении, а о понимании происходящего.
Поскольку все работает, как ожидается, когда WorkerThread.setWork()
является synchronized
или когда WorkerThread.workToDo
поле установлено volatile
Я подозреваю проблему памяти.
Но почему именно это происходит? Отладка не помогает, как только вы начинаете шаг за шагом, все ведет себя так, как ожидалось.
Объяснение будет оценено.
Я думаю, что здесь также существует проблема кеш-когерентности. Создание 'workToDo'' volatile' или создание 'setWork' для' synchronized' будет исправлять когерентность кеша, заставляя значение 'workToDo' быть видимым для всех потоков. Тем не менее, это не решит условие гонки, которое вы определили, для чего потребуется блокировка или другая конструкция, чтобы предотвратить вызов другого потока из 'setWork()', в то время как этот поток находится между 'workToDo.run()' и 'workToDo = null '. –