Приложение Java зависло при использовании синхронизированного ключевого слова - PullRequest
3 голосов
/ 19 февраля 2012

У меня есть класс, который запускает несколько потоков.Каждый поток (расширяет поток) вызывает новый экземпляр класса WH, в классе WH есть переменная, которая должна использоваться всеми потоками.Таким образом, иерархия выглядит следующим образом:

class S extends Thread {
....
....
  WH n = new WH(args);
....
....
}

Теперь у класса WH есть переменная, которая должна использоваться совместно, объявленная как:

private static volatile Integer size;

One of the functions tries to access size through Synchronized:
Synchronized (size) { // Program gets stuck at this line
 ... stuff ...
}

Она застревает, даже если я порождаю только один поток,Есть идеи, почему это происходит?(К вашему сведению - я не хочу использовать AtomicInteger на основе моего выбора дизайна)

Спасибо

Ответы [ 2 ]

2 голосов
/ 19 февраля 2012

Ваша проблема в том, что блокировка Нефинал ссылка на переменную имеет бесполезную семантику .

Каждый раз, когда вы видите, что что-то делает synchronized(var);, а var является экземпляром или статической переменной и не помечается final, это ошибка, потому что все может прийти и сделать var = new Thing();и теперь есть как минимум 2 потока, которые могут работать с этим блоком одновременно, это логическая ошибка без исключений.Каждый контролер стиля Java lint помечает это как критическую ошибку, просто потому, что компилятор не улавливает это, не означает, что он в любом случае полезен.

В этом случае вы раскрываете эту бесполезную семантикуизменение значения неизменяемого Integer класса.

Ваша Integer переменная size не является окончательной и равна Immutable, что означает, что каждый раз, когда вы ее изменяете, вы должны изменить ссылку на новый объект, который представляет новое значение, и каждый поток получит новые и разные ссылки для привязки.Таким образом, без блокировки .

Используйте private static final AtomicInteger size = new AtomicInteger();

И тогда вы можете synchronize(size);, поскольку size теперь final, вы можете изменить его на месте иполучить предполагаемую и правильную семантику.

или вы можете synchronize(some_other_final_reference); и использовать обычный int, пока эта ссылка, на которую синхронизируется, равна final и может находиться в области действия любого потока, который долженприобрести ручку к нему, он будет работать.

Лично я бы использовал AtomicInteger, так как он более сплоченный, вы фиксируете то, что вы не хотите менять, с помощью какого-либо другого потока, самодокументирования и ясных намерений.

1 голос
/ 19 февраля 2012

Я не могу использовать AtomicInteger, так как мне нужно получить значение размера, проверить условие на нем и увеличить или нет на основе условия.Так что я должен получить, а затем, возможно, увеличить его.Мне все еще нужна блокировка в этом случае.

Я считаю, что то, что вы описываете, - это то, что AtomicInteger определенно может сделать без блокировки с помощью метода compareAndSet(), нет?Хотя единственный поддерживаемый тест - это равенство, так что, возможно, это не сработает для вас.

Кроме того, если вы планируете синхронизацию с переменной, нет необходимости также делать ее volatile.

...