Ответ
Паттерн Prototype не является потокобезопасным, потому что операция клонирования (clone()) и последующая модификация состояния объекта-прототипа могут выполняться конкурентно разными потоками, что приводит к состоянию гонки (race condition).
Основные причины:
- Разделяемое состояние: Если прототип содержит изменяемые (mutable) поля, они разделяются между оригиналом и клонами или между разными клонами.
- Неатомарность
clone(): МетодObject.clone()выполняет поверхностное копирование (shallow copy). Для сложных объектов требуется глубокое копирование (deep copy), реализация которого также должна быть синхронизирована. - Отсутствие синхронизации: Стандартная реализация не использует блокировки (
synchronized) или другие механизмы координации потоков.
Пример проблемы:
public class DocumentPrototype implements Cloneable {
private StringBuilder content; // Изменяемый объект!
public DocumentPrototype(String text) {
this.content = new StringBuilder(text);
}
public void append(String text) {
this.content.append(text); // Небезопасно для потоков
}
@Override
public DocumentPrototype clone() {
try {
// Поверхностное копирование! Оба объекта ссылаются на один StringBuilder.
return (DocumentPrototype) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
// Поток 1: prototype.append("A");
// Поток 2: DocumentPrototype copy = prototype.clone(); // copy.content может быть в несогласованном состоянии
Решение: Для потокобезопасности необходимо:
- Использовать глубокое копирование неизменяемых (immutable) объектов.
- Синхронизировать методы, изменяющие состояние прототипа, или использовать
ThreadLocal. - Рассмотреть использование фабрики для создания копий с полной инициализацией.