2012-05-11 4 views
3

Безопасна ли/допустимая практика блокировки частной переменной поля (вместо использования объекта блокировки)? Таким образом, у меня могут быть разные блокировки для разных целей. Пример ниже:Множественные блокировки объектов в Java?

class Test { 
    private Integer x = 0; 
    private Integer y = 0; 

    public void incrementX() { 
    synchronized(x) { 
     x++; 
    } 
    } 

    public void decrementX() { 
    synchronized(x) { 
     x++; 
    } 
    } 

    public void incrementY() { 
    synchronized(y) { 
     y++; 
    } 
    } 

    public void decrementY() { 
    synchronized(y) { 
     y++; 
    } 
    } 

Должен ли я иметь объект блокировки для каждого частного члена, которого я хочу заблокировать? Пример:

class Test { 
    private final Object xLock = new Object(); 
    private final Object yLock = new Object(); 
    private Integer x = 0; 
    private Integer y = 0; 

... 

} 

Или я должен просто иметь один общий замок и использовать его для всех частных переменных, которые требуют блокировки? Пример:

class Test { 
    private final Object objLock = new Object(); 
    private Integer x = 0; 
    private Integer y = 0; 

... 

} 
+1

для примитивов, рассмотрите варианты Atomic *, например. AtomicInteger – maress

ответ

7

Остерегайтесь, чтобы всегда использовать окончательный член вар для замка! Например, если вы используете Integer, и вы планируете его изменить, это будет очень плохой практикой, так как каждый звонок увидит другой объект и вызовет гонку данных.

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

Если у вас есть два ортогональных операции над вашим объектом, которые могут произойти одновременно, не вызывая каких-либо datarace, это случай для двух замков. В вашем примере есть два целых числа, каждый из которых меняется независимо. Я вижу это как случай для двух замков. Если у вас был более сложный код, где, по крайней мере, в одной операции вам нужно было получить доступ к обеим целым, что бы связать их вместе, а затем вам понадобится один замок.

+0

мы не можем синхронизировать на примитивах? – maress

+0

@maress: нет, вы не можете. Мониторы - это свойство объектов, примитивы не являются объектами, поэтому у них нет мониторов. –

+0

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

6

Это вполне приемлемо для блокировки частного поля, если это поле является объектом. Примитивы не имеют встроенной блокировки, поэтому первый фрагмент недействителен.

Однако я бы избежал блокировки в закрытом поле, если это поле доступно снаружи (например, с использованием геттера), поскольку это позволит любому заблокировать один и тот же объект для разных целей. Таким образом, второе решение является самым чистым, ИМХО.

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

EDIT:

теперь, когда вы изменили свой вопрос и использование оболочки объектов, запирающий на частных случаях Integer действительно не является хорошим решением, как вы измените значения этих переменных внутри методов. Используйте последние поля как locks.

Помните, что x++, если x является экземпляром Integer, эквивалентно:

int temp = x.intValue(); 
temp++; 
x = Integer.valueOf(temp); 

Кроме того, поскольку Integer.valueOf() кэширует экземпляры Integer, вы можете иметь несколько классов, используя тот же экземпляр Integer для блокировки совершенно разные вещи. Рецепт медленного выполнения и взаимоблокировок.

+0

Обратите внимание, что вы фактически не «блокируете личное поле». Вы * всегда * блокируете объект (поэтому примитивы не работают). Обычно различие не важно, но если объект, на который ссылается поле, изменяется, то он может стать * чрезвычайно важным. –

0

Я думаю, у вас должно быть два разных замка для двух полей. Вы блокируете объект, чтобы предотвратить одновременный доступ двух или нескольких потоков к одному и тому же объекту.

Вы также можете посмотреть на объект блокировки в Java http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Lock.html Это более производительное, чем синхронизация, и в java.util.concurrent есть некоторые утилиты класс для работы с замками (также ReadWriteLock, если вы необходимо)

0

AFAIK объект блокировки, который вы используете, является только идентификатором. Я имею в виду, вы можете использовать любой объект, который хотите. Единственное, что важно: «если две вещи должны быть взаимоисключающими, тогда они должны использовать один и тот же замок».

Таким образом, подход использования самого собственного var выглядит нормально.

НО, помните!

  • Я не думаю, что вы можете заблокировать на примитив, он должен быть Object
  • Если изменить значение поля, следующий процесс приобретет другой замок !!!

Таким образом, отдельный замок кажется более безопасным. Кроме того, вы абсолютно уверены, что ваше поле не изменится (на самом деле вы должны объявить его final).

Смежные вопросы