来自核心java的原子问题

时间:2011-05-21 05:42:55

标签: java concurrency atomic

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

注意:

public void flipDone() {
   done = !done;
}

//不是原子

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

5 个答案:

答案 0 :(得分: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

在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

flipDone方法被调用了两次。 donefalse转到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

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

答案 1 :(得分:6)

这里有三个步骤:

  1. 读取布尔值done
  2. 的值
  3. 反转在步骤1中读取的值
  4. 将该值分配给布尔值done
  5. 没有什么可以阻止另一个线程在所有这些中间预先占用。

答案 2 :(得分:4)

因为两个线程可能同时调用flipDone()方法,所以done变量的状态是不确定的。

答案 3 :(得分:4)

执行时

done = !done;

实际发生的是:

1. Get the value of done
2. Apply the not operator
3. Assign it to done

如果两个线程一起执行第一步,它们将具有相同的完成值,因此它们不会更改两次,而是只更改一次。

例如,如果最初完成为true,则在将其更改两次之后,您会发现它仍然是真的,但如果两个线程一起执行步骤1,则它将为false。

答案 4 :(得分:0)

它不是作为单个不可分割的操作执行,而是三个离散操作的序列 - 获取当前的完成值,取消值,将新值写回完成。这是一个read-modify-write操作,其结果状态来自先前的状态。