2011-08-22 4 views
6

Должны ли быть все поля, включая суперполя, целенаправленно неизменяемого класса java «final», чтобы быть потокобезопасным или достаточно, чтобы не иметь методов модификатора?Конечные поля и безопасность потоков

Предположим, у меня есть POJO с нефинальными полями, где все поля являются типом некоторого неизменяемого класса. Этот POJO имеет getters-seters и конструктор, который устанавливает начальное значение. Если я расширю этот POJO с помощью методов модификации нокаута, тем самым сделав его неизменным, класс расширения будет потокобезопасным?

+2

Что вы подразумеваете под * выбиванием модификаторов методов * методов-модификаторов? Выбрасывание исключения из всех сеттеров? Это нарушит [принцип Лисковской замены] (http://en.wikipedia.org/wiki/Liskov_substitution_principle). Но да, этот класс ** ** был бы потокобезопасным. –

+0

Да, бросая исключение во время выполнения или переопределяя их с пустым телом, возможно, с некоторым протоколированием. Я знаю, что это нарушает LSP. – pcjuzer

ответ

11

Чтобы использовать эффективно неизменяемый объект без полей final в потоковом безопасном режиме, вам нужно использовать одну из безопасных идиом публикации, когда объект был доступен для других потоков после инициализации, в противном случае эти потоки могут видеть объект в частично инициализированном состояние (от Java Concurrency in Practice):

  • Инициализация ссылки на объект от статического инициализатора;
  • Хранение ссылки на него в поле volatile или AtomicReference;
  • Хранение ссылки на это в конечном поле правильно построенного объекта; или
  • Хранение ссылки на него в поле, которое должным образом защищено блокировкой.

Декларирования поля вашего неизменного объекта как final релизы этого ограничения (т.е. он гарантирует, что если другие потоки видят ссылку на объект, они также видят его final поля в полностью инициализировано состоянии). Однако в общем случае он не гарантирует, что другие потоки могут видеть ссылку на объект, как только он будет опубликован, поэтому вам все равно придется использовать безопасную публикацию для ее обеспечения.

Обратите внимание, что если ваш объект реализует интерфейс, вы можете использовать подход, используемый Collections.unmodifiableList() и т.д:

class ImmutableFooWrapper implements IFoo { 
    private final IFoo delegate; // final provides safe publication automatically 

    public ImmutableFooWrapper(IFoo delegate) { 
     this.delegate = delegate; 
    } 
    ... 
} 

public IFoo immutableFoo(IFoo foo) { 
    return new ImmutableFooWrapper(foo); 
} 
+0

Спасибо за подробный ответ. Я думал об одном и том же решении с использованием делегата, но потом я всегда возвращался к решению с расширением, потому что, думаю, «супер» ссылка также является окончательной. Что мне не хватает? – pcjuzer

+0

@pcjuzer: 'super' не имеет ничего общего с параллелизмом, так что в случае расширения вам все равно понадобится безопасная публикация. – axtavt

+0

Итак, это означает, что если вы объявляете финал поля, то если он обновляется одним потоком, то другой поток не увидит его, и если он увидит, то он увидит в обновленном состоянии? – AKS

-2

Да, это будет неизменен и, следовательно, поточно-но только до тех пор, как поля частный.

+2

Почему поля действительно должны быть частными? – xSNRG

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