java线程同步

Reg*_*ser 14 java multithreading synchronization

在下面的类中,方法getIt() 线程安全,为什么?

public class X { 
  private long myVar; 
  public void setIt(long  var){ 
    myVar = var; 
   }  
   public long getIt() { 
     return myVar; 
  } 
}
Run Code Online (Sandbox Code Playgroud)

Phi*_*oss 22

它不是线程安全的.类型longdoubleJava中的变量被视为两个独立的32位变量.一个线程可以写入,并且当另一个线程读取两个半部分时写入一半的值.在这种情况下,读者会看到一个永远不会存在的值.

要使此线程安全,您可以声明myVarvolatile(Java 1.5或更高版本)或同时生成setItgetIt synchronized.

请注意,即使myVar是32位,int您仍然可能遇到线程问题,其中一个线程可能正在读取另一个线程已更改的过期值.这可能是因为CPU已缓存该值.要解决此问题,您还需要声明myVarvolatile(Java 1.5或更高版本)或同时生成setItgetIt synchronized.

值得注意的是,如果您getIt在后续setIt调用中使用结果,例如x.setIt(x.getIt() * 2),那么您可能希望synchronize跨越两个调用:

synchronized(x)
{
  x.setIt(x.getIt() * 2);
}
Run Code Online (Sandbox Code Playgroud)

如果没有额外的同步,另一个线程可能会更改getItsetIt调用之间的值,从而导致其他线程的值丢失.


tan*_*ens 9

这不是线程安全的.即使您的平台保证原子写入long,缺少synchronized使得一个线程可能调用setIt(),甚至在此调用完成后,另一个线程可以调用,getIt()并且此调用可能返回旧值myVar.

synchronized关键字不仅仅是对一个块或方法的一个线程的独占访问.它还保证第二个线程被告知变量的变化.

因此,您必须将两种方法都标记为synchronized或将成员标记myVarvolatile.

有一个关于同步很好的解释在这里:

原子动作不能交错,因此可以使用它们而不必担心线程干扰.但是,这并不能消除所有同步原子操作的需要,因为仍然可能存在内存一致性错误.使用volatile变量可降低内存一致性错误的风险,因为对volatile变量的任何写入都会建立与之后读取同一变量的先发生关系.这意味着对volatile变量的更改始终对其他线程可见.更重要的是,它还意味着当线程读取volatile变量时,它不仅会看到volatile的最新更改,还会看到导致更改的代码的副作用.


Ano*_*on. 6

不,这不对.至少,不在缺乏原子64位内存访问的平台上.

假设线程A调用setIt,将32位复制到后备值所在的内存中,然后在它可以复制其他32位之前进行抢占.

然后线程B调用getIt.

  • 即使在提供原子64位内存访问的平台上,也必须同步get和set方法.或者你必须使成员易变.有关详细信息,请参阅我的回答 (7认同)