Как переменная flag
используется несколькими потоками, необходим некоторый механизм для обеспечения видимости изменений. Это действительно общая картина в многопоточности в целом. Модель памяти Java иначе не гарантирует, что в другом потоке будет отображаться новое значение flag
.
Это позволяет оптимизировать использование современных многопроцессорных систем, где сохранение когерентности кэша всегда может быть дорогостоящим. Доступ к памяти обычно является более медленным, чем другие «обычные» операции с процессором, поэтому современные процессоры идут на очень большие расстояния, избегая его как можно больше. Вместо этого часто используемые места хранятся в небольшой, быстрой локальной процессорной памяти - кеше. Изменения делаются только в кеше, а сбрасывает в основную память в определенные моменты. Это отлично подходит для одного процессора, так как содержимое памяти не изменяется другими сторонами, поэтому мы гарантируем, что содержимое кеша отражает содержимое памяти. (Ну, это просто упрощение, но с точки зрения программирования на высоком уровне, по моему мнению, не имеет значения). Проблема в том, что, как только мы добавляем другой процессор, независимо изменяя содержимое памяти, эта гарантия теряется. Чтобы смягчить эту проблему, были разработаны различные (иногда разработанные - см., Например, here) протоколы согласования кэш-памяти. Неудивительно, что для этого требуются некоторые служебные и межпроцессорные коммуникации.
Другие, некоторые из связанных вопросов atomicity операций записи. В принципе, даже если изменения видны другими потоками, можно увидеть частично. Обычно это не проблема в java, поскольку спецификация языка гарантирует атомарность всех записей. Тем не менее, пишет на 64-битные примитивов (long
и double
) явно указанных следует рассматривать как две отдельные, 32-битный пишет:
Для целей модели памяти языка программирования Java, одну запись на энергонезависимое длинное или двойное значение рассматривается как две отдельные записи: одна на каждую 32-битную половину. Это может привести к ситуации, когда поток видит первые 32 бита 64-битного значения из одной записи, а второй 32 бита из другой записи. (JLS 17.7)
Назад к коду в вопросе ... синхронизация не требуется, и synchronized
блок удовлетворяет потребность. Тем не менее, я считаю, что сделать такие флагов volatile
более приятным решением. Чистый эффект тот же - гарантия видимости и атомная запись - но он не загромождает код небольшими synchronized
блоками.
Не только это, но первый фрагмент кода должен всегда удерживать блокировку во время проверки флага. – user2357112
, добавив к вышеуказанному комментарию, 'wait' должен находиться в цикле' while' – Pranalee
Предлагаю вам прочитать [javadoc] (http://docs.oracle.com/javase/7/docs/api/java/lang/Object. html # wait% 28% 29), чтобы увидеть канонический шаблон, который вы должны использовать с 'wait'. – assylias