Здесь есть две проблемы: безопасная публикация, а затем последующая безопасность потока для mutate (shuffle()
), за которой следует чтение (get(0)
).
Я не думаю, что ваш кодовый адрес является безопасной публикацией вообще. Для этого используйте volatile
или final
, а для volatile
выполняйте присваивание последним в ctor.
public class Generator {
private final List<Integer> list = new ArrayList<>();
public Generator() {
for (int i = 0; i < 1000; i++) {
list.add(i);
}
}
Или
public class Generator {
private volatile List<Integer> list;
public Generator() {
List<Integer> temp = = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
temp.add(i);
}
list = temp;
}
Теперь я думаю, что вы можете синхронизироваться на list
(или любом другом объекте), чтобы обеспечить безопасность потоков. Вы должны убедиться, что get
находится внутри синхронизированного блока. Обратите внимание, что в вашем коде есть ошибка, и на самом деле он не выдает уникальных чисел.
public Integer generate() {
synchronized( list ) {
Collections.shuffle(list);
return list.get(0);
}
}
Или
public synchronized Integer generate() {
Collections.shuffle(list);
return list.get(0);
}
См. Брайан Гетц Параллелизм Java на практике для получения дополнительной информации, особенно Безопасная публикация. SO также кратко обсуждает шаблон безопасной публикации .
Получение уникальных чисел - это просто логика, не связанная с безопасностью потоков. Сначала перемешайте только один раз в ctor, затем просто увеличьте счетчик, чтобы вернуть числа по порядку. Помните, что в вашем списке только 1000 номеров, поэтому вы можете вернуть не более 1000 уникальных номеров.
private int index;
public synchronized Integer generate() {
if( index >= 1000 ) throw IllegalArgumentError();
return list.get( index++ );
}