Являются ли окончательные немодифицируемые множества потоками безопасными?

Извините за любительский вопрос, но поскольку javadocs не совсем понятны об этом, мне было интересно, неумолимые наборы потокобезопасны? Или я должен беспокоиться о проблемах с внутренним состоянием concurrency?

Set<String> originalSet = new HashSet<>();
originalSet.add("Will");
originalSet.add("this");
originalSet.add("be");
originalSet.add("thread");
originalSet.add("safe?");
final Set<String> unmodifiableSet = Collections.unmodifiableSet(originalSet);
originalSet = null; // no other references to the originalSet

// Can unmodifiableSet be shared among several threads?  

Я наткнулся на кусок кода со статическим, доступным только для чтения Set, который делится на несколько потоков... Оригинальный автор написал что-то вроде этого:

mySet = Collections.synchronizedSet(Collections.unmodifiableSet(originalSet));

И затем каждый поток обращается к нему с кодом, например:

synchronized (mySet) {
  // Iterate and read operations
}

По этой логике только один поток может работать на Set сразу... Поэтому мой вопрос заключается в том, что для немодифицируемого набора при использовании таких операций, как для каждого, contains, size и т.д., Действительно ли мне нужно синхронизировать доступ?

Ответ 1

Если это немодифицируемый Set<String>, в соответствии с вашим примером, тогда вы в порядке; потому что объекты String неизменяемы. Но если это набор чего-то, что не является неизменным, вы должны быть осторожны в отношении двух потоков, пытающихся изменить один и тот же объект внутри набора.

Вы также должны быть осторожны, есть ли ссылка где-то в Set, которая не является немодифицируемой. Возможно, что переменная немодифицируется, но все же имеет в виду a Set, который может быть изменен с помощью другой переменной; но ваш пример, кажется, покрыл.

Ответ 2

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

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

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