Синхронизация на объекте в Java, затем изменение значения синхронизируемой переменной - PullRequest
21 голосов
/ 15 июля 2011

Я сталкивался с кодом, подобным этому

synchronized(obj) {

   obj = new Object();

}

Что-то не так в этом, я не могу объяснить, в порядке ли этот фрагмент кода или в нем действительно что-то не такэто из.Спасибо

Ответы [ 4 ]

20 голосов
/ 15 июля 2011

Это, вероятно, не то, что вы хотите сделать. Вы синхронизируете объект, на который больше нет ссылки. Рассмотрим другой поток, выполняющий этот метод: они могут войти и попытаться снять блокировку в тот момент, когда ссылка на obj была обновлена ​​для указания на новый объект. В этот момент они синхронизируются с другим объектом, чем первый поток. Это, вероятно, не то, что вы ожидаете.

Если у вас нет веских причин не делать этого, вы, вероятно, захотите синхронизировать конечный объект (для наглядности.) В этом случае вам, вероятно, потребуется использовать отдельную переменную блокировки. Например:

class Foo
{
    private final Object lock = new Object();
    private Object obj;

    public void method()
    {
        synchronized(lock)
        {
            obj = new Object();
        }
    }
}
3 голосов
/ 15 июля 2011

Это тот случай, когда кто-то может подумать, что он делает это нормально, но, вероятно, это не то, что он хотел.В этом случае вы синхронизируете текущее значение в переменной obj.Как только вы создадите новый экземпляр и поместите его в переменную obj, условия блокировки изменятся.Если это все, что происходит в этом блоке, он, вероятно, будет работать, но если он будет делать что-то еще после этого, объект не будет должным образом синхронизирован.или полностью на другом мьютексе.

2 голосов
/ 04 мая 2017

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

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

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

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

Соответствующий раздел в JLS: 14.19 .Поток, выполняющий синхронизированный оператор:

1) оценивает выражение, затем

2) получает блокировку для значения, к которому вычисляется выражение, затем выполняется

3)блок.

Он не пересматривает этап оценки снова, когда успешно устанавливает блокировку.

Этот код не работает.Не делай этого.Зафиксируйте вещи, которые не меняются.

0 голосов
/ 04 мая 2017

Это необычное использование, но, похоже, оно действительно в тех же сценариях.Один, который я нашел в кодовой базе JmDNS:

public Collection<? extends DNSEntry> getDNSEntryList(String name) {
    Collection<? extends DNSEntry> entryList = this._getDNSEntryList(name);
    if (entryList != null) {
        synchronized (entryList) {
            entryList = new ArrayList<DNSEntry>(entryList);
        }
    } else {
        entryList = Collections.emptyList();
    }
    return entryList;
}

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

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