Подтвердить что ты не робот

Синхронизация объекта в java, а затем изменение значения синхронизированной переменной

Я столкнулся с таким кодом, как этот

synchronized(obj) {

   obj = new Object();

}

Что-то не так хорошо, я не могу объяснить, Является ли этот фрагмент кода ОК или что-то действительно не так в нем, пожалуйста, укажите его. Благодаря

4b9b3361

Ответ 1

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

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

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

    public void method()
    {
        synchronized(lock)
        {
            obj = new Object();
        }
    }
}

Ответ 2

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

Лучше быть безопасным и синхронизировать на содержащем объекте или на другом мьютексе целиком.

Ответ 3

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

(Проводя это, потому что другие ответы недостаточно просты - "вероятно" здесь недостаточно, и не хватает деталей.)

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

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

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

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

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

3) выполняет блок.

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

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

Ответ 4

Это необычное использование, но, похоже, имеет смысл в тех же сценариях. Один я нашел в кодовой базе 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;
}

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