[英]Why in certain circumstances, updates from a thread are not visible although the update is made?
I'm reading " Effective Java " and in the chapter where he's talking about threads, I stepped into this snippet: 我正在阅读“ 有效的Java ”,在他谈论线程的这一章中,我进入了这个片段:
private static int nextSerialNumber = 0;
public static int generateSerialNumber(){
return nextSerialNumber++;
}
A bit later, talking that snippet but in case there is no synchronization, he says: 过了一会儿,他说了那段代码,但是如果没有同步,他说:
More surprisingly, it is possible for one thread to call
generateSerialNumber
repeatedly, obtaining a sequence of serial numbers from zero to n, after which another thread callsgenerateSerialNumber
and obtains a serial number of zero.更令人惊讶的是,一个线程可能重复调用
generateSerialNumber
,获得从零到n的序列号序列,然后另一个线程调用generateSerialNumber
并获得零序列号。 Without synchronization, the second thread might see none of the updates made by the first.如果没有同步,则第二个线程可能看不到第一个线程所做的任何更新。 This is a result of the aforementioned memory model issue.
这是上述内存模型问题的结果。
I can't understand how this is possible. 我不明白这怎么可能。 For the thread to obtain "a sequence of serial numbers from zero to n", the increment must be done, otherwise the thread will read always the same value.
为了使线程获得“从零到n的序列号序列”,必须完成增量,否则线程将始终读取相同的值。 If the increment is done, then the variable is set, because being an
int
, the writing is atomic. 如果增量完成,则将设置变量,因为它是
int
,所以写入是原子的。 So, if the static variable is changed by a thread, although it might be the same one, another thread must be able to read that value. 因此,如果一个线程更改了静态变量,尽管它可能是相同的,但另一个线程必须能够读取该值。 So how is possible that another thread, calling
generateSerialNumber
, can obtain a serial number of zero? 那么另一个线程调用
generateSerialNumber
怎么可能获得零序列号呢?
because being an
int
, the writing is atomic.因为是
int
,所以写作是原子的。
That just means it's not possible for another thread to see a half-updated value, it's either the old one or the new one (in 64-bit systems this extends to long
variables). 这只是意味着另一个线程不可能看到一半更新的值,它是旧的还是新的(在64位系统中,这扩展到
long
变量)。
It has nothing to do with the basic visibility problem meaning that unless your variable is volatile
or you're using synchronization
the value can and will be cached by different threads for performance purposes and you will see old cached values instead of up to date ones. 它与基本的可见性问题无关,这意味着除非您的变量是
volatile
或您使用synchronization
,否则该值可以并且将由不同的线程缓存以提高性能,并且您将看到旧的缓存值,而不是最新的值。
In addition, nextSerialNumber++;
另外,
nextSerialNumber++;
is not atomic as it consists of read-update-write
steps, so making nextSerialNumber
volatile won't fix this code. 不是原子的,因为它由
read-update-write
步骤组成,因此将nextSerialNumber
volatile不会解决此代码。 The method needs to be synchronized
. 该方法需要
synchronized
。
The current value of nextSerialNumber
might be cached in a cache local to the core, and updates to that value might also be cached for a while until they are flushed to main memory. nextSerialNumber
的当前值可能会缓存在核心本地的缓存中,对该值的更新也可能会缓存一段时间,直到刷新到主内存中为止。 So when using multiple threads, scheduled on different cores, they might have their own local cached version of nextSerialNumber
. 因此,当使用安排在不同内核上的多个线程时,它们可能具有
nextSerialNumber
自己的本地缓存版本。
When not explicitly instructed, the code (and CPU) will assume it is fine to use this local cached version, and happily read and update the cached variable, while another thread scheduled on a different core will happily do the same with its own cached version. 如果未明确指示,则代码(和CPU)将假定可以使用此本地缓存版本,并愉快地读取和更新缓存变量,而在不同内核上调度的另一个线程将对其自己的缓存版本高兴地执行相同操作。
When using concurrency primitives like synchronized
and volatile
, this changes. 当使用并发原语(例如,
synchronized
和volatile
,这会改变。 For a synchronized
-block (simplified) the Java implementation will make sure those values will be retrieved from main memory when first read and written back to main memory at the end of a synchronized
block, it does the same for volatile
variables, but then at each read and write. 对于
synchronized
块(简化),Java实现将确保在synchronized
块末尾首次读取并写回主存储器时,将从主存储器中检索这些值,对volatile
变量也是如此,但是在每次读写。
In reality things are a bit more complicated, with happens-before relations between threads, etc. But basically, your question boils down to "blame caching". 实际上,事情有些复杂,发生在线程之间的关系之前,等等。但是,基本上,您的问题归结为“怪罪缓存”。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.