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

时间:2010-02-03 14:57:56

标签: java synchronization

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

6 个答案:

答案 0 :(得分:11)

  

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

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

final!= immutable

 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();
  

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

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

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

属于final和immutable的属性(如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 
    }
 }

答案 1 :(得分:5)

  

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

取自Synchronized Methods

答案 2 :(得分:1)

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

private final String message = "Hello, World";

// No need to mark as synchronized as message can never change.
public String getMessage() {
  return message;
}

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

private final Object lock;

...

synchronized(lock) {
  // Do something in critical section.
}

答案 3 :(得分:1)

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

例如:

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;
   }
}

答案 4 :(得分:1)

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

private final List<Foo> foos = new ArrayList<Foo>();


public void addFoo(Foo newFoo){
      foos.add(newFoo);
}

public Foo popFoo(){
     return foos.remove(0);
}

在这种情况下,即使foos是最终的,但是当线程A试图删除元素时,线程B可以添加Foo,从而导致可能存在图标的状态。

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

答案 5 :(得分:0)

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

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

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