Назначение переменной Java Lock перед использованием. Зачем? - PullRequest
7 голосов
/ 05 ноября 2011

Во многих источниках Java (например, LinkedBlockingDeque) я вижу такие вещи:

final ReentrantLock lock = new ReentrantLock();

public void putLast(E e) throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
       // do stuff
    } finally {
        lock.unlock();
    }
}

Я понимаю базовый шаблон (блокировка, разблокировка в конечном итоге), но мой вопрос зачем назначать переменную Lock с локальной областью действия перед ее использованием? Почему это вместо следующего?

final ReentrantLock lock = new ReentrantLock();

public void putLast(E e) throws InterruptedException {
    this.lock.lock();
    try {
       // do stuff
    } finally {
        lock.unlock();
    }
}

Повлияет ли это на оптимизацию?Может ли первый пример предотвратить укрупнение блокировки?

РЕДАКТИРОВАТЬ после комментариев : Пожалуйста, не добавляйте ответ, если вы действительно не знаете, почему это так.Это из источника Java, тег @author - Даг Ли, так что я уверен, что он есть по какой-то причине.Пожалуйста, не указывайте, что код просто эквивалентен.

Спасибо

Ответы [ 5 ]

7 голосов
/ 05 ноября 2011

Когда вы назначаете локальную переменную в методе, компилятор может выполнить некоторые оптимизации.см. В ArrayBlockingQueue, зачем копировать поле последнего члена в локальную конечную переменную?

2 голосов
/ 05 ноября 2011

Немного покопавшись в коде, я нашел примеры обоих способов от того же автора, Дуга Ли:

  • LinkedBlockingDeque (начиная с JDK 1.6) использует метод "прямого доступа".
  • CopyOnWriteArrayList (начиная с JDK 1.5) использует метод "локальной переменной".

Есть больше примеров для каждой идиомы в java.util.concurrent, но кажется, что для каждого класса был выбран непротиворечивый стиль.

Обратите внимание, что во всех соответствующих случаях поле lock было объявлено final. Это самая важная часть, потому что семантика модели памяти для конечных полей немного особенная в JVM (см. JLS) .

Опираясь на это: получение локальной копии или нет не влияет на правильность многопоточности.

Также обратите внимание, что Dough Lea выбрала более короткий стиль в новом коде (как показано в примерах). Так что, возможно, идиома «взять локальную копию» - это то, что осталось от дней до того, как java.util.concurrent был частью JDK и до того, как модель памяти JVM была принята соответствующим образом. Я предполагаю, что код до этого принятия мог бы выглядеть так:

public void op(){
    ReentrantLock lock = getLock();
    lock.lock();
    try {
        realOp();
    } finally {
        lock.unlock();
    }
}

где getLock() содержал грубую многопоточную безопасную логику.

2 голосов
/ 05 ноября 2011

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

Но поскольку блокировка является окончательной, это не имеет значения: назначение локальной переменной в вашем первом примере является излишним.

1 голос
/ 06 ноября 2011

Hotspot не оптимизирует конечные поля экземпляра.

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

Я сам делаю локальную переменную «грязный код» большую часть времени, если знаю / ожидаю, что это горячий код.

Дополнительно info, вкл. Даг Ли - личное мнение по этому вопросу.

0 голосов
/ 05 ноября 2011

Хотя это и не должно иметь никакого значения, в списках рассылки было указано, что на реальных JRE наблюдаются небольшие различия в производительности.

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

...