2015-02-04 2 views
2

Изучение потоков и параллелизма. Рассмотрим следующий код:Недостатки вложенных синхронизированных блоков

class A { 

    protected final Object lock = new Object(); 

    public void remove(Object obj){ 
    synchronized(lock){ 
     // remove the object 
    } 
    } 

    public void add(Object obj){ 
    synchronized(lock){ 
     // add the object 
    } 
    } 
} 

Этот код потокобезопасно в том смысле, что нет двух разных потоков могут add или remove в то время как один поток находится в процессе добавления или удаления.

Теперь рассмотрим следующий sublcass А:

class B extends A { 

    public void update1(Object original, Object newObj){ 
    remove(original); 
    add(original); 
    } 

    public void update2(Object original, Object newObj){ 
    synchronized(lock) { 
     remove(original); 
     add(newObj); 
    } 
    } 

} 

Класс B должен реализовать поточно-update метод. Теперь, насколько я знаю, update1 не является потокобезопасным, потому что операция не является атомарной, т. Е. Нет синхронизации между выполнением remove и add (исправьте меня, если не так).

update2 правильный способ реализации поточно-безопасного метода update? Есть ли недостатки наличия вложенных синхронизированных блоков по сравнению с тем же lock?

+0

Я думаю, что такой код чреват опасностью тупика. Вам будет лучше использовать более современные дополнения к JVM: параллельный пакет, ExcecutorService и parallelStream, если вы до JDK 8. – duffymo

+0

Оба «update1» и «update2» являются потокобезопасными. Наличие 'synchronized (lock)' in 'update2' является излишним, потому что вы уже настроили синхронизацию в методах класса A. После того, как поток обработает блокировку, он может проходить через весь следующий блок «synchronize» без необходимости его приобретения. – ortis

+2

@ortis Я думаю, что они хотят удалить и добавить, чтобы сделать транзакцию. В 'update1' другой поток может удалить или добавить между вызовами. –

ответ

6

update2 Правильный способ реализации поточно-безопасного метода обновления?

Да, это так. Вы достигли атомарности, и вы совместимы с вызывающими абонентами add и remove методами.

Есть ли недостатки наличия вложенных синхронизированных блоков по одной и той же блокировке?

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

0

Использование synchronized делает весь объект «заблокированным»: никакие другие потоки не могут вызывать какие-либо методы, в то время как другой поток использует один из методов синхронизации объекта.

Это может привести к замедлению работы некоторых программ.

В качестве альтернативы вы можете использовать ReentrantLock.

Вот как он применяется для кода в посте:

class A { 
    protected final Lock lock = new ReentrantLock(); 

    public void remove(Object obj){ 
    lock.lock(); 
    try { 
     // remove the object 
    } finally { 
     lock.unlock(); 
    } 
    } 

    public void add(Object obj){ 
    lock.lock(); 
    try { 
     // add the object 
    } finally { 
     lock.unlock(); 
    } 
    } 
} 

class B extends A { 
    // ... 

    public void update2(Object original, Object newObj){ 
    lock.lock(); 
    try { 
     remove(original); 
     add(newObj); 
    } finally { 
     lock.unlock(); 
    } 
    } 
} 
+0

Вызов метода «synchronized» не «блокирует» объект. Он предотвращает одновременную синхронизацию других потоков на одном и том же объекте. В вашем примере используются разные способы достижения _exactly_ того же результата, что и пример @fernandohur. –

0

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

Реентрантного замок является замок, который может быть , даже если замок уже достигнут той же нитью.

Почему бы вам не предоставить removeAndAdd в методе суперкласса, если есть сценарий, который должен удалить и добавить атомный.

Убедитесь, что ваш объект блокировки отмечен как частным, так и последним по соображениям безопасности.

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