2014-10-04 2 views
2

Вот короткие немного текста из Oracle Java Tutorials:Почему имяList.add не синхронизировано?

«Синхронные заявления

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

public void addName(String name) { 
    synchronized(this) { 
     lastName = name; 
     nameCount++; 
    } 
    nameList.add(name); 
} 

В этом примере метод addName необходимо синхронизировать изменения в LastName и nameCount, но также необходимо, чтобы избежать синхронизирующих вызовов других объектов методы. (Вызов методов других объектов из синхронизированного кода может создавать проблемы, описанные в разделе «Личность».) Без синхронизированных инструкций должен быть отдельный, несинхронизированный метод с единственной целью вызвать nameList.add. »

Я понимаю их точку зрения о гибкости Synchronized дает. Но почему Oracle решил, что имяList.add не нужно синхронизировать? В более общем плане, как я могу определить, какие методы объектов нужно синхронизировать, а какие нет?

+1

Что такое 'nameList'? –

ответ

0

Синхронизация имеет свою цену, высокую производительность. Общее эмпирическое правило (которое сам JDK также следует и в этом случае) не синхронизирует, если это абсолютно необходимо.

Поскольку существует множество случаев, когда вы хотите добавить элемент в список, не требуя синхронизации (например, если указанный список является локальной переменной), он не был определен как синхронизированный. Когда вам нужно синхронизировать такую ​​операцию, вы всегда можете использовать CopyOnWWriteArrayList или явно синхронизировать свой доступ.

+0

Я думаю, вы неправильно поняли вопрос (так же, как я сделал на первый взгляд). Это не спрашивает, почему 'ArrayList' и другие коллекции не синхронизированы. Скорее, он спрашивает, почему в примере кода строка 'nameList.add()' не включена в блок 'synchronized'. –

+0

C-A правильный. Мне кажется, что если nameList не является потокобезопасным, код не будет безопасным. Я думаю, что если nameList не является потокобезопасным, он должен находиться в синхронизированном блоке. Я прав? Кроме того, как nameList может быть локальной переменной? Это явно не локальная переменная в addName. Если бы это была локальная переменная в любом другом методе, она не была бы в области видимости. Какие еще существуют случаи, когда вы хотите добавить элемент в список, не требуя синхронизации? – ClarkS

0

Синхронизировать, исходя из того, что вы знаете темы, которые получат доступ.

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

Итак:

synchronized(this.nameList){ 
//now I'm safe to modify 
    nameList.add(name); 
} 
+1

Даже если другие потоки не могут получить доступ или изменить список, не приведет ли этот код к ошибкам согласования памяти, если nameList не является потокобезопасной коллекцией? – ClarkS

0

Вы можете создать Потокобезопасная ArrayList цепочек, как это:

ArrayList<String> list = Collections.synchronizedList(new ArrayList<String>()); 

методы добавления() и удалить() потокобезопасны. Если вам не нужно перебирать элементы, вы просто в порядке.

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

synchronized (list) { 
    Iterator i = list.iterator(); // Must be in the synchronized block 
    while (list.hasNext()) 
    yourmethod(list.next()); // Your logic on elements... 
} 

Перед использованием прочитать документы здесь: Synchronized Collection

+0

Я вижу, что nameList может быть потокобезопасным. Но если бы это был ArrayList, это не было бы потокобезопасным. Если бы он был объявлен как ArrayList, будет ли код безопасным? Я не уверен, но я так не думаю. Не была бы вероятность ошибок согласованности памяти, если nameList был ArrayList, даже если nameList не был доступен? – ClarkS

+0

@ClarkS способ объявить поток безопасным ArrayList - это тот, который я показываю вам выше в строке 1. Если вы объявите его просто как ArrayList list = new new ArrayList (); то безопасность потока не гарантируется. Просто объявите его с помощью Collections.synchronizedList, как указано выше, и вы можете добавить его в безопасное место. – Alboz

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