我们是否同步最终的实例变量? 如果是,那么使用是什么?

我想知道我们是否同步最终的实例变量。 由于变量是最终的,因此价值不会发生变化。 任何人都能解释一下吗?

我们是否同步最终的实例变量?

是。 您仍然需要同步可变对象

最终!=不可变的

public class Something { private final List list = new ArrayList(); public void add( String value ) { this.list.add( value ); } public int count() { return this.list.size(); } } 

后来

  Something s = new Something(); Thread a = new Thread() { public void run(){ s.add( "s" ); s.add( "s" ); s.add( "s" ); } }; Thread b = new Thread(){ public void run() { s.count(); s.count(); } }; a.start(); b.start(); 

如果是,那么使用是什么?

您声明,在对象生命周期内无法更改给定属性。

这增加了安全性(您的对象属性不能被恶意子类替换)允许编译器优化(因为它知道将只使用引用)防止子类更改它等等。

最终和不可变的属性(如String,Integer和其他)不需要同步。

 class X { private final String name = "Oscar"; public int nameCount(){ return this.name.length(); //<-- Will return 5 ALWAYS ( actually the compiler might inline it } } 

一个重要的例外:构造对象后无法修改的final字段,一旦构造了对象,就可以通过非同步方法安全地读取

取自同步方法

如果方法返回最终变量的值,通常不会将访问器方法标记为已synchronized ; 例如

 private final String message = "Hello, World"; // No need to mark as synchronized as message can never change. public String getMessage() { return message; } 

但是,您可能希望同步最终变量,例如,如果您将其用作锁定; 例如

 private final Object lock; ... synchronized(lock) { // Do something in critical section. } 

如果实例变量不是不可变的,那么即使它是最终的,该变量的状态仍然可以改变。 举个例子:

 private final List foos = new ArrayList(); public void addFoo(Foo newFoo){ foos.add(newFoo); } public Foo popFoo(){ return foos.remove(0); } 

在这种情况下,即使foos是final,也可以通过线程B添加Foo ,而线程A尝试删除元素,从而导致潜在的图标化状态。

其他示例中提到的Sychronized Methods教程是正确的,因为读取最终变量不需要进行同步。 但是,如果变量是可变的(如List ),则必须同步写入该变量以保证“之前发生”关系。 但是,如果变量是不可变的,那么当然不允许写入该变量,因此不需要同步。

嗯,是。 当您使用引用类型声明最终实例变量时,引用是不可变的,但它引用的对象通常是可变的。 如果可能有多个线程访问和更新可变对象的状态,则需要同步最终实例上的操作。

例如:

 public class Thing { private final List l1 = private final List l2 = private final List l3 = ... public void l1ToL2() { l2.add(l1.removeFirst()); } public void l2ToL3() { l3.add(l2.removeFirst()); } } 

如果我们不做某些事情来同步这些方法对l1l2l3 ,它们就不是线程安全的,并且来自不同线程的并发操作可能会破坏列表。

另一方面,这是线程安全的,出于@Anthony Forloney的回答中所述的原因。

 public class Thing { private final int i = ... ; public int getI() { return i; } } 

无需同步对最终实例变量的访问。

请参阅http://java.sun.com/docs/books/tutorial/essential/concurrency/syncmeth.html

同步方法启用了一种简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是通过同步方法完成的。 (一个重要的例外:在构造对象后无法修改的最终字段,一旦构造了对象,就可以通过非同步方法安全地读取)这种策略是有效的,但是可能会带来活性问题,因为我们将会请参阅本课后面的内容。