Я пытался преднамеренно создать проблемы видимости с потоками, и я получил неожиданные результаты:
public class DownloadStatus {
private int totalBytes;
private boolean isDone;
public void increment() {
totalBytes++;
}
public int getTotalBytes() {
return totalBytes;
}
public boolean isDone() {
return isDone;
}
public void done() {
isDone = true;
}
}
public class DownloadFileTask implements Runnable {
DownloadStatus status;
public DownloadFileTask(DownloadStatus status) {
this.status = status;
}
@Override
public void run() {
System.out.println("start download");
for (int i = 0; i < 10_000; i++) { //"download" a 10,000 bytes file each time you run
status.increment(); //each byte downloaded - update the status
}
System.out.println("download ended with: " + status.getTotalBytes()); //**NOTE THIS LINE**
status.done();
}
}
//creating threads, one to download, another to wait for the download to be done.
public static void main(String[] args) {
DownloadStatus status = new DownloadStatus();
Thread t1 = new Thread(new DownloadFileTask(status));
Thread t2 = new Thread(() -> {
while (!status.isDone()) {}
System.out.println("DONE!!");
});
t1.start();
t2.start();
}
Итак, выполнение этого создаст проблему видимости - второй поток не будет посмотрите обновленное значение, так как оно кэшировало его до того, как оно было записано первым потоком - это вызывает бесконечное (пока) l oop, второй поток постоянно проверяет кэшированный isDone (). (по крайней мере, я так думаю, что это работает).
Я не понимаю, почему эта проблема с видимостью перестает возникать, когда я закомментирую строку из второго блока кода, который вызывает status.getTotalBytes()
. Насколько я понимаю, оба потока начинают с кэширования объекта состояния как есть, поэтому второй поток должен постоянно проверять свое кэшированное значение (и, по сути, не видеть новое значение, обновленное первым потоком).
Почему эта строка вызывая метод в объекте статуса, вызывающий эту проблему видимости? (и что еще интереснее - почему бы не позвонить, чтобы это исправить?)