2015-07-06 4 views
3

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

private final Object lock = new Object(); 
private volatile Object reference = null; 

public Object getOrCompute(Supplier<Object> supplier) { 
    if (reference == null) { 
     synchronised(lock) { 
      if (reference == null) { 
       reference = supplier.get(); 
      } 
     } 
    } 

    return reference; 
} 

Мои ожидания в том, что данный новый экземпляр этого класса, множественные вызовы getOrCompute() будет только когда в результате одного поставщика будет вызван, и результатом этого поставщика является результатом всех звонки (и будущие звонки) до getOrCompute().

+2

Согласно [этой статье] (http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html) некоторыми довольно тяжелыми именами Java ваш код является потокобезопасным благодаря 'volatile' Справка. – Keppil

+4

Я надеюсь, что 'поставщик.get()' никогда не сможет вернуть 'null'. –

+0

@AndyBrown В чем проблема, если он возвращает null –

ответ

3

Это безопасно, потому что все, что сделано в supplier.get(), не должно быть переупорядочено с присвоением reference. (Или, точнее, он не должен быть переупорядочен, когда вы делаете неустойчивое чтение reference.)

Блокировка обеспечивает исключительность, а изменчивая семантика записи/чтения обеспечивает видимость. Обратите внимание, что это было верно только после того, как Java 5, который был выпущен давно, но вы по-прежнему найдете устаревшие статьи в Интернете о том, как блокировка с двойной проверкой (это официальное название этой идиомы) т работы. Они были правы в то время, но теперь они устарели.

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

+0

, изменчивость ссылки не имеет отношения к обеспечению безопасности потоков. – Vogel612

+0

@ Vogel612 I не означает, что это было (см. первые три слова моего ответа :)). Это в стороне. – biziclop

+0

Или, по крайней мере, я не ** подразумевал ** подразумевать это. Возможно, я сделал это случайно :) – biziclop

0

Синхронизация не является потокобезопасной. Это мешает потокам получать доступ к объекту сразу, но у него нет контроля над тем, какой поток получает его, когда или что он делает с объектом, как только он получит доступ к нему. Синхронизация ограничивает доступ только к одному потоку в то время, поток, к которому он сначала обращается, сначала получает доступ к нему.

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

Представьте, что у вас есть один поток, обращающийся к методу и создающий объект, он извлекает его и, пока он извлекает объект, другой поток пытается его создать, с которым он не будет допущен, поскольку он существует, поэтому он может прыгать прямо для извлечения объекта, как и поток номер один, теперь они могут изменять объект одновременно, ergo, а не потокобезопасным. Но создание нового объекта является потокобезопасным в том смысле, что объект может быть создан только один раз.

+2

'В этом случае единственное, что он делает, - это предотвращение создания нескольких потоков для объекта.« Это намного больше, чем это, что обеспечивает видимость поставленного объекта для всех последующих вызывающих. Какое определение «безопасно». – biziclop

+0

Да, я набросал то, что он делает внутри синхронизированного блока, как «instantialization», потому что когда вы подталкиваете его к краю, это то, что он делает; он заменяет значение/ссылку «reference» на все, что находится в 'поставщик.get()', так что часть IS является потокобезопасной; только один поток может это сделать. Но объект 'reference' не является потокобезопасным и может быть изменен, однако требуемые им потоки хотят. – Gemtastic

+2

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

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