2011-08-22 29 views
6

它应该是所有领域,包括超级领域,有目的不可变的java类'final'为了线程安全或足够没有修饰符方法吗?最终字段和线程安全

假设我有一个非final字段的POJO,其中所有字段都是一些不可变类的类型。这个POJO有getters-setters,并且有一个构造函数来设置一些初始值。如果我通过敲除修饰符方法来扩展此POJO,从而使其不可变,扩展类将是线程安全的吗?

+2

你是什么意思*敲出修饰符方法*修饰符方法?抛出所有setter的异常?这会违反[Liskov替代原则](http://en.wikipedia.org/wiki/Liskov_substitution_principle)。但是,是的,这个类会**是线程安全的。 –

+0

是的,抛出一个运行时异常或用一个空的主体覆盖它们可能有一些日志记录。我知道这是违反LSP的。 – pcjuzer

回答

11

为了以线程安全的方式使用不带final字段的有效不可变对象,需要在初始化后使对象对其他线程可用时使用安全发布习惯用法之一,否则这些线程可以看到部分初始化的对象状态(从Java Concurrency in Practice):

  • 初始化从静态初始化的对象引用;
  • 将引用存储到易失性字段或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