Является ли этот способ обнаружения сердечных сокращений безопасным и последовательным? - PullRequest
3 голосов
/ 08 ноября 2008

Этот вопрос обсуждался в двух сообщениях в блоге (http://dow.ngra.de/2008/10/27/when-systemcurrenttimemillis-is-too-slow/, http://dow.ngra.de/2008/10/28/what-do-we-really-know-about-non-blocking-concurrency-in-java/),, но я пока не получил однозначного ответа. Если у нас есть одна тема, которая делает это:

public class HeartBeatThread extends Thread {
  public static int counter = 0;
  public static volatile int cacheFlush = 0;

  public HeartBeatThread() {
    setDaemon(true);
  }

  static {
    new HeartBeatThread().start();
  }

  public void run() {   
    while (true) {     
      try {
        Thread.sleep(500);
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }

      counter++;
      cacheFlush++;
    }
  }
}

И многие клиенты, которые запускают следующее:

if (counter == HeartBeatThread.counter) return;
counter = HeartBeatThread.cacheFlush;

это потокобезопасно или нет?

Ответы [ 2 ]

5 голосов
/ 08 ноября 2008

В модели памяти Java? Нет, ты не в порядке.

Я видел несколько попыток пойти по пути очень «мягкого сброса», но без явного забора вы определенно играете с огнем.

Семантика «случается раньше» в

http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.7

начать ссылаться на чисто межпоточные действия как «действия» в конце 17.4.2. Это приводит к путанице, так как до этого момента они различали действия между потоками и внутри потоков. Следовательно, внутрипотоковое действие манипулирования счетчиком явно не синхронизируется между изменчивым действием отношением «происходит до». У вас есть два потока рассуждений о синхронизации, один управляет локальной согласованностью и подвергается всем тонкостям анализа псевдонимов и т. Д. Для операций тасования, другой касается глобальной согласованности и определяется только для межпоточных операций.

Один для логики внутри потока, которая говорит, что внутри потока чтения и записи последовательно переупорядочиваются, и один для логики между потоками, которая говорит такие вещи, как volatile чтения / записи, и что синхронизация начинается / заканчивается соответствующим образом ограждена.

Проблема заключается в том, что видимость энергонезависимой записи не определена, поскольку она является внутрипотоковой операцией и поэтому не охватывается спецификацией. Процессор, на котором он работает, должен видеть его так, как он выполнял эти операторы последовательно, но его секвенизация для целей между потоками потенциально не определена.

Теперь, реальность того, может ли это повлиять на вас, - это совсем другое дело.

Во время работы Java на платформах x86 и x86-64? Технически вы находитесь в темной территории, но практически очень сильные гарантии гарантируют места x86 для операций чтения и записи, включая общий порядок чтения / записи при доступе к cacheflush и локальный порядок для двух записей, и эти две операции чтения должны включить этот код чтобы выполнить правильно, если это делает его через компилятор unmoled. Это предполагает, что компилятор не вмешивается и пытается использовать свободу, разрешенную по стандарту, для переупорядочения операций над вами из-за доказуемого отсутствия псевдонимов между двумя внутрипотоковыми операциями.

Если вы перейдете в память с более слабой семантикой выпуска, как ia64? Тогда ты вернулся сам по себе.

Тем не менее, компилятор может совершенно честно разбить эту программу на Java на любой платформе. То, что он функционирует прямо сейчас, является артефактом текущих реализаций стандарта, а не стандарта.

Кроме того, в CLR модель времени выполнения сильнее, и этот вид уловки является законным, потому что отдельные записи из каждого потока имеют упорядоченную видимость, поэтому будьте осторожны, пытаясь перевести любые примеры оттуда.

1 голос
/ 08 ноября 2008

Ну, я не думаю, что это так.

Первое выражение if:

if (counter == HeartBeatThread.counter) 
    return;

Не имеет доступа к любому изменчивому полю и не синхронизируется. Таким образом, вы можете читать устаревшие данные вечно и никогда не достигнуть точки доступа к изменчивому полю.

Цитата из одного из комментариев во второй записи блога: «все, что было видно потоку A, когда он записывает в изменяемое поле f, становится видимым для потока B, когда он читает f». Но в вашем случае B (клиент) никогда не читает f (= cacheFlush). Поэтому изменения в HeartBeatThread.counter не должны становиться видимыми для клиента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...