Ami*_* Z. 5 java concurrency multithreading volatile
我正在尝试学习Java中多线程使用的术语.所以如果我在下面的文字中使用了错误的定义,请纠正我:
原子动作: 根据Java doc:
在编程中,原子动作是一次有效发生的动作.原子动作不能在中间停止:它要么完全发生,要么根本不发生.在动作完成之前,原子动作的副作用是不可见的.
这就是为什么读或写长变量或双变量不是原子的.因为它涉及两个操作,第一个32位和第二个32位读/写变量.另外,从上面的段落中我了解到,如果我synchronized在方法上使用它,它将使该方法成为原子(理论上讲).
易变变量:也来自Java Doc:
这意味着对volatile变量的更改始终对其他线程可见.更重要的是,它还意味着当线程读取volatile变量时,它不仅会看到volatile的最新更改,还会看到导致更改的代码的副作用.
现在,根据Joshua Bloch的Effective Java第2版,请考虑以下关于volatile声明的书中提到的要点:
考虑以下:
// Broken - requires synchronization!
private static volatile int nextSerialNumber = 0;
public static int generateSerialNumber() {
return nextSerialNumber++;
}
Run Code Online (Sandbox Code Playgroud)
方法的状态由单个可原子访问的字段nextSerialNumber组成,该字段的所有可能值都是合法的.因此,不需要同步来保护其不变量.但是,如果没有同步,该方法将无法正常工作.
这是因为nextSerialNumber++它执行读取,增量,写入时不是原子的.
所以,如果nextSerialNumber++不是原子的,需要synchronize.那么为什么以下是原子的并且不需要同步?
private static volatile long someNumber = 0;
public static int setNumber(long someNumber) {
return this.someNumber = someNumber;
}
Run Code Online (Sandbox Code Playgroud)
我不明白为什么使用volatile double或者long使其原子化?
因为volatile它确实是确保线程B是否尝试读取long线程A正在写入的变量,并且线程A只写了32位,那么当线程B访问资源时,它将获得32由线程A编写的位数.这不会使其成为原子,因为Java Doc中解释了术语atomic的定义.
Sto*_*ica 10
我不明白为什么在double或long上使用volatile会使它成为原子?
在不使用volatile关键字的情况下,您可能会读取a的前32位double或long由一个线程写入,而另一个32位由另一个线程写入,称为单词撕裂,并且显然不是原子的.
该volatile关键字可以确保不会发生.您读取的64位值将是由一个线程写入的值,而不是由多个线程写入的某些Franken值.这意味着由于volatile关键字,这些类型变为原子.
该volatile关键字不能进行动作等x++原子,而不管类型(64位或32位),因为它是一个复合操作(读+增量+写),而不是一个简单的写入.a中涉及的操作x++可以通过其他线程的操作进行交织.该volatile关键字不能作这样的混合操作的原子.
所以如果
nextSerialNumber++不是原子的,需要同步.那么为什么以下是原子的并且不需要同步?Run Code Online (Sandbox Code Playgroud)private static volatile long someNumber = 0; public static int setNumber(long someNumber) { return this.someNumber = someNumber; }
nextSerialNumber++ 需要同步,因为它是复合操作,因此不是原子操作.
this.someNumber = someNumber原则归功于this.someNumber存在volatile,并且赋值操作也是原子的,是一个单独的操作.因此无需同步.没有volatile,this.someNumber不能以原子方式编写,因此需要同步.