Мне всегда было трудно убедительно проиллюстрировать проблемы параллелизма: хорошо, хорошо, все хорошо о случается до и прочем, но почему это должно волновать? Есть ли реальная проблема? Есть много плохо написанных, плохо синхронизированных программ, и они все еще работают большую часть времени.
Раньше я находил курорт в " работах" большую часть времени VS работает", но, честно говоря, это слабый подход. Так что мне нужен был пример, который сделал бы разницу очевидной - и, желательно, болезненной.
Итак, вот версия, которая действительно показывает разницу:
public class VolatileExample implements Runnable {
public static boolean flag = true; // do not try this at home
public void run() {
long i = 0;
while (flag) {
if (i++ % 10000000000L == 0)
System.out.println("Waiting " + System.currentTimeMillis());
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new VolatileExample());
thread.start();
Thread.sleep(10000L);
flag = false;
long start = System.currentTimeMillis();
System.out.println("stopping " + start);
thread.join();
long end = System.currentTimeMillis();
System.out.println("stopped " + end);
System.out.println("Delay: " + ((end - start) / 1000L));
}
}
Простой прогон показывает:
Waiting 1319229217263
stopping 1319229227263
Waiting 1319229242728
stopped 1319229242728
Delay: 15
То есть требуется более десяти секунд (15 здесь) для работающего потока, чтобы заметить, что было любое изменение.
С volatile
вы получаете:
Waiting 1319229288280
stopping 1319229298281
stopped 1319229298281
Delay: 0
то есть, выход (почти) немедленно. Разрешение currentTimeMillis
составляет около 10 мс, поэтому разница составляет более 1000 раз.
Обратите внимание, что это была версия Apple JK (ранее) Sun с опцией -server
. Было добавлено 10-секундное ожидание, чтобы JIT-компилятор узнал, что цикл достаточно горячий, и оптимизировал его.
Надеюсь, это поможет.