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

Java 我们是否同步最终的实例变量?如果是,那有什么用?,java,synchronization,Java,Synchronization,我想知道我们是否同步了最终的实例变量。由于变量是最终变量,因此值不能更改。 有人能解释一下吗 一个重要的例外:构建对象后无法修改的final字段可以在构建对象后通过非同步方法安全地读取 取自无需同步对最终实例变量的访问 看 同步方法支持防止线程干扰和内存一致性错误的简单策略:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都通过同步方法完成。(一个重要的例外:构建对象后无法修改的最终字段可以通过非同步方法安全读取,一旦构建了对象)此策略是有效的,但可能会出现活跃性问题,正如我们将在本课

我想知道我们是否同步了最终的实例变量。由于变量是最终变量,因此值不能更改。 有人能解释一下吗

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


取自

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


同步方法支持防止线程干扰和内存一致性错误的简单策略:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都通过同步方法完成。(一个重要的例外:构建对象后无法修改的最终字段可以通过非同步方法安全读取,一旦构建了对象)此策略是有效的,但可能会出现活跃性问题,正如我们将在本课后面看到的那样。

如果方法返回最终变量的值,则通常不会将访问器方法标记为已同步;e、 g

private final String message = "Hello, World";

// No need to mark as synchronized as message can never change.
public String getMessage() {
  return message;
}
但是,您可能希望对最终变量进行同步,例如,如果您将其用作锁;e、 g

private final Object lock;

...

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

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

例如:

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());
   }
}
private final List<Foo> foos = new ArrayList<Foo>();


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

public Foo popFoo(){
     return foos.remove(0);
}
如果我们不同步这些方法对
l1
l2
l3
的使用,它们就不是线程安全的,来自不同线程的并发操作可能会损坏列表

另一方面,这是线程安全的,原因在@Anthony Forloney的回答中陈述

public class Thing {
   private final int i = ... ;

   public int getI() {
       return i;
   }
}
我们是否同步最终的实例变量

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

决赛!=不变的

 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和不可变的属性(如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 
    }
 }
X类{
私有最终字符串name=“Oscar”;
public int name count(){

返回此.name.length();//如果实例变量不是不可变的,则该变量的状态仍然可以更改,即使它是final。例如:

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());
   }
}
private final List<Foo> foos = new ArrayList<Foo>();


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

public Foo popFoo(){
     return foos.remove(0);
}
private final List foos=new ArrayList();
公共无效添加Foo(Foo newFoo){
foos.add(newFoo);
}
公共Foo popFoo(){
返回foos.remove(0);
}
在这种情况下,即使
foos
是final,线程B也可以在线程a试图删除元素时添加
Foo
,从而导致潜在的iconsistent状态


其他示例中提到的同步化方法教程是正确的,因为读取最终变量不需要同步化。但是,如果变量是可变的(如
列表
所示),则必须同步写入该变量,以确保“之前发生”关系。但是,如果变量是不可变的,那么当然无论如何都不允许写入该变量,因此不需要同步。

应该不需要同步最终变量。谁会这样做?final ArrayList myList=new ArrayList();您能实现线程安全的putIfAbsent(对象obj)吗不使用同步?