来自核心java的原子问题

Ter*_*ser 2 java concurrency atomic

这是Core Java第8版第757页的一节

警告:

public void flipDone() {
   done = !done;
}
Run Code Online (Sandbox Code Playgroud)

//不是原子的

我不明白为什么它不是原子的.谁能告诉我为什么?谢谢

Bin*_*mas 7

flipDone方法由计算机以三个不同的步骤执行:

Read the value of memory location labeled done into ALU
Flip the value (i.e true -> false or false -> true)
Write the new value into the memory location done
Run Code Online (Sandbox Code Playgroud)

在Java中,可以在多个线程中调用一段代码.应该将这些线程视为同时执行代码.

比如说,标记的内存位置最初done包含该值false.考虑两个线程调用flipDone,产生以下步骤序列:

Thread 1                             Thread 2
-----------------------------------------------------------------------
Read value of done, false
Invert that value to true
Write the new value, true, to done       
                                     Read value of done, now true
                                     Invert that value to false
                                     Write the new value, false, to done
Run Code Online (Sandbox Code Playgroud)

flipDone方法被调用了两次.done从那里false开始true然后再回到false- 正如人们所期望的那样.但由于线程并发执行,这不是步骤的唯一顺序.请考虑以下顺序:

Thread 1                             Thread 2
-----------------------------------------------------------------------
Read value of done, false
Invert that value to true            Read value of done, false
Write the new value, true, to done   Invert that value to true    
                                     Write the new value, true, to done
Run Code Online (Sandbox Code Playgroud)

当第一个线程反转它读取的值时,第二个线程同时读取该值.类似地,当第一个线程将值写入内存时,第二个线程正在反转它读取的值.当Thread 2完成后,价值done将是真实的.在这里,虽然flipDone被叫了两次,但done只翻了一次!其中一个更新似乎已经丢失.这是本书试图警告你的问题.


dle*_*lev 6

这里有三个步骤:

  1. 读取布尔值 done
  2. 反转步骤1中读取的值
  3. 将该值分配给布尔值 done

没有什么可以阻止另一个线程在所有这些中间预先占用.