2015-03-09 3 views
2

В книге «Java Concurrency на практике» упоминается, что следующий код не поточно:Почему ConcurrentHashMap работа в перепроверили Блокировка

@NotThreadSafe 
public class DoubleCheckedLocking { 

    private static Resource resource; 

    public static Resource getInstance(){ 
     if(resource == null){ 
      synchronized (DoubleCheckedLocking.class){ 
       if(resource == null) 
        resource = new Resource(); 
      } 
     } 
     return resource; 
    } 
} 

Это не поточно, потому что из-за: - один поток может создать новый экземпляр ресурса - другой поток в то же время в состоянии «если» может получить непустую ссылку, но объект ресурса не будет полностью инициализирован

В этом question аналогичный код. Ресурсы хранятся в concurentHashMap, и люди говорят, что это threadSafe. Что-то вроде этого:

public class DoubleCheckedLocking2 { 

    private static ConcurrentHashMap<String, ComplexObject> cache = new ConcurrentHashMap<String, ComplexObject>(); 

    public static ComplexObject getInstance(String key) { 
     ComplexObject result = cache.get(key); 
     if (result == null) { 
      synchronized (DoubleCheckedLocking2.class) { 
       ComplexObject currentValue = cache.get(key); 
       if (currentValue == null) { 
        result = new ComplexObject(); 
        cache.put(key, result); 
       } else { 
        result = currentValue; 
       } 
      } 
     } 
     return result; 
    } 
} 

Почему хранить значения в ConcurrentHashMap сделать код THREADSAFE? Я думаю, что все еще возможно, что ComplexObject не будет полностью инициализирован, и этот «частичный объект» будет сохранен на карте. И другие потоки будут читать частичные не полностью инициализированные объекты.

Я думаю, что знаю, что происходит «раньше», я проанализировал код в JDK 8.0_31, и я до сих пор не знаю ответа.

Я знаю функции как computeIfAbsent, putIfAbsent. Я знаю, что этот код может быть написан по-разному. Я просто не знаю деталей, которые делают этот код потокобезопасным.

ответ

4

Бывает раньше На самом деле это ключ. Есть происходит до край, простирающийся от map.put(key, object) до последующего map.get(key), поэтому объект, который вы извлекаете, по крайней мере так же современен, как и в то время, когда он был сохранен на карте.

+0

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

+0

. Мой ответ подразумевает, что он * является потокобезопасным из-за края * происходит до *. Все, что делается в результате написания нити до размещения на карте, будет гарантировано наблюдаемым потоком чтения после того, как он получит объект. В этом суть транзитивности * происходит до * отношения. Возможно, вы упустили тот факт, что * происходит до того, как * закрывается по заказу программы? –

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