Предотвращение потерянного обновления в Java без прямой синхронизации - PullRequest
4 голосов
/ 19 мая 2009

Мне интересно, возможно ли избежать проблемы потерянного обновления, когда несколько потоков обновляют одну и ту же дату, избегая использования synchronized(x) { }.

Я буду делать многочисленные добавления и приращения:

val++;
ary[x] += y;
ary[z]++;

Я не знаю, как Java скомпилирует их в байт-код и может ли поток быть прерван в середине одного из этих операторов блоков байт-кода. Другими словами, являются ли эти заявления потокобезопасными?

Кроме того, я знаю, что класс Vector синхронизирован, но я не уверен, что это значит. Будет ли следующий код потокобезопасным в том смысле, что значение в позиции i не изменится между vec.get(i) и vec.set(...).

class myClass {
  Vector<Integer> vec = new Vector<>(Integer);

  public void someMethod() {
    for (int i=0; i < vec.size(); i++)
      vec.set(i, vec.get(i) + value);
  }
}

Заранее спасибо.

Ответы [ 2 ]

6 голосов
/ 19 мая 2009

Для целей многопоточности ++ и += рассматриваются как две операции (четыре для double и long). Так что обновления могут забить друг друга. Не просто один, а планировщик, действующий в неподходящий момент, может уничтожить миллисекунды обновлений.

java.util.concurrent.atomic твой друг.

Ваш код можно сделать безопасным, если вы не возражаете против того, чтобы каждый элемент обновлялся отдельно, и вы не меняете размер (!), Как:

for (int i=0; i < vec.size(); i++) {
    synchronized (vec) {
        vec.set(i, vec.get(i) + value);
    }
}

Если вы хотите добавить изменение размера к Vector, вам нужно переместить оператор synchronized за пределы цикла for, и вы также можете просто использовать обычный новый ArrayList. На самом деле не очень много пользы для синхронизированного списка.

Но вы можете использовать AtomicIntegerArray:

private final AtomicIntegerArray ints = new AtomicIntegerArray(KNOWN_SIZE);
[...]
    int len = ints.length();
    for (int i=0; i<len; ++i) {
        ints.addAndGet(i, value);
    }
}

Преимущество в том, что нет замков (!) И нет бокса. Реализация тоже довольно забавная, и вам нужно понять, что она выполняет более сложные обновления (например, генераторы случайных чисел).

1 голос
/ 19 мая 2009

vec.set () и vec.get () являются потокобезопасными, поскольку они не будут устанавливать и извлекать значения таким образом, чтобы потерять наборы и получить в других потоках. не означает, что ваш сет и ваш выигрыш будут происходить без перерыва.

Если вы действительно собираетесь писать код, как в приведенных выше примерах, вам, вероятно, стоит что-то заблокировать. И synchronized(vec) { } так же хорош, как и любой. Здесь вы просите, чтобы две операции выполнялись синхронно, а не только одна потокобезопасная операция.

Даже java.util.concurrent.atomic обеспечит безопасное выполнение только одной операции (получение или установка). Вам нужно увеличивать и увеличивать за одну операцию.

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