简体   繁体   English

为什么在某些情况下,尽管进行了更新,但仍看不到来自线程的更新?

[英]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 calls generateSerialNumber 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. 当使用并发原语(例如, synchronizedvolatile ,这会改变。 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.

相关问题 ThreadLocal:为什么一个线程所做的更改在另一个线程中可见 - ThreadLocal: why changes made by one thread is visible in other thread 强制在线程内部进行GUI更新 - JSlider更新 - Forcing a GUI update inside of a thread - JSlider updates 为什么要等待线程延迟更新组件的更新? - Why is waiting for a thread delaying updates to swing components? 在某些情况下处理RuntimeExceptions有效吗? - Handling RuntimeExceptions in certain circumstances valid? 为什么变量在同步中对其他线程不可见? - Why variable is not visible to other thread in synchronization? Hibernate没有注意到从其他源创建的数据库更新 - Hibernate doesn't notice database updates made from other source 尽管已填充,但从单独的线程访问时ArrayList为空。 - ArrayList empty when accessed from separate thread although it is populated. 从线程更新JFreeChart - Update JFreeChart from thread 有没有办法在某些情况下创建 ArrayList? - Is there a way to create an ArrayList under certain circumstances? 在连续 java 之间对共享上下文 object 所做的最新更改是否对执行 lambda 的每个线程始终可见 - Are latest changes made to shared context object between consecutive java CompletionStages always visible to each thread executing the lambda
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM