volatile 关键字有什么用?
今天在工作中,我遇到了Java中的易失性
关键字。 不太熟悉它,我发现 这个解释。
鉴于该文章详细解释了相关关键字,您是否曾经使用过它,或者您是否见过可以以正确方式使用该关键字的情况?
At work today, I came across the volatile
keyword in Java. Not being very familiar with it, I found this explanation.
Given the detail in which that article explains the keyword in question, do you ever use it or could you ever see a case in which you could use this keyword in the correct manner?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(25)
是的,我经常使用它 - 它对于多线程代码非常有用。 你提到的那篇文章是一篇很好的文章。 尽管有两件重要的事情需要记住:
完全理解它的作用
以及它与同步有何不同。
在许多情况下会出现波动,
从表面上看,要更简单一些
性能替代
同步,通常更好
对 volatility 的理解会让
明确同步是唯一的
可行的选项。
尽管有很多较旧的 JVM
同步的。 我记得看到过一个文档,其中引用了不同 JVM 中的各种级别的支持,但不幸的是我现在找不到它。 如果您使用的是 Java 1.5 之前的版本,或者您无法控制程序将在其上运行的 JVM,那么一定要研究一下它。
Yes, I use it quite a lot - it can be very useful for multi-threaded code. The article you pointed to is a good one. Though there are two important things to bear in mind:
completely understand what it does
and how it differs to synchronized.
In many situations volatile appears,
on the surface, to be a simpler more
performant alternative to
synchronized, when often a better
understanding of volatile would make
clear that synchronized is the only
option that would work.
lot of older JVMs, although
synchronized does. I remember seeing a document that referenced the various levels of support in different JVMs but unfortunately I can't find it now. Definitely look into it if you're using Java pre 1.5 or if you don't have control over the JVMs that your program will be running on.
虽然我在这里提到的答案中看到了许多很好的理论解释,但我在这里添加了一个实际示例和解释:
1.
代码运行而不使用易失性使用
在上面的代码中,有两个线程 - Producer 和消费者。
生产者线程在循环上迭代 5 次(其间睡眠 1000 毫秒或 1 秒)。 在每次迭代中,生产者线程将 sCount 变量的值增加 1。因此,生产者在所有迭代中将 sCount 的值从 0 更改为 5。
消费者线程处于恒定循环中,每当 sCount 的值发生变化时就会打印,直到值达到 5 时结束。
两个循环同时启动。 所以生产者和消费者都应该打印 sCount 的值 5 次。
OUTPUT
ANALYSIS
在上面的程序中,当生产者线程更新 sCount 的值时,它确实更新了主内存(每个线程所在的内存)中变量的值将首先读取变量的值)。 但使用者线程仅在第一次从该主内存中读取 sCount 的值,然后将该变量的值缓存在其自己的内存中。 因此,即使主内存中原始 sCount 的值已被生产者线程更新,消费者线程也会从其未更新的缓存值中读取。 这称为可见性问题。
2.
使用 VOLATILE USE 运行代码
在上面的代码中,将声明 sCount 的代码行替换为以下代码:
OUTPUT
ANALYSIS
当我们声明一个变量为 volatile,这意味着对该变量或从该变量的所有读取和写入都将直接进入主内存。 这些变量的值永远不会被缓存。
由于 sCount 变量的值永远不会被任何线程缓存,因此消费者始终从主内存中读取 sCount 的原始值(其中由生产者线程更新)。 因此,在这种情况下,两个线程打印 sCount 的不同值 5 次,输出是正确的。
这样, volatile 关键字就解决了 VISIBILITY PROBLEM 。
While I see many good Theoretical explanations in the answers mentioned here, I am adding a practical example with an explanation here:
1.
CODE RUN WITHOUT VOLATILE USE
In the above code, there are two threads - Producer and Consumer.
The producer thread iterates over the loop 5 times (with a sleep of 1000 milliSecond or 1 Sec) in between. In every iteration, the producer thread increases the value of sCount variable by 1. So, the producer changes the value of sCount from 0 to 5 in all iterations
The consumer thread is in a constant loop and print whenever the value of sCount changes until the value reaches 5 where it ends.
Both the loops are started at the same time. So both the producer and consumer should print the value of sCount 5 times.
OUTPUT
ANALYSIS
In the above program, when the producer thread updates the value of sCount, it does update the value of the variable in the main memory(memory from where every thread is going to initially read the value of variable). But the consumer thread reads the value of sCount only the first time from this main memory and then caches the value of that variable inside its own memory. So, even if the value of original sCount in main memory has been updated by the producer thread, the consumer thread is reading from its cached value which is not updated. This is called VISIBILITY PROBLEM .
2.
CODE RUN WITH VOLATILE USE
In the above code, replace the line of code where sCount is declared by the following :
OUTPUT
ANALYSIS
When we declare a variable volatile, it means that all reads and all writes to this variable or from this variable will go directly into the main memory. The values of these variables will never be cached.
As the value of the sCount variable is never cached by any thread, the consumer always reads the original value of sCount from the main memory(where it is being updated by producer thread). So, In this case the output is correct where both the threads prints the different values of sCount 5 times.
In this way, the volatile keyword solves the VISIBILITY PROBLEM .
绝对没错。 (不仅在 Java 中,在 C# 中也是如此。)有时,您需要获取或设置一个保证在给定平台上是原子操作的值,例如 int 或 boolean,但不要求线程锁定的开销。 易失性关键字允许您确保在读取值时获得当前值,而不是刚刚因另一个线程上的写入而过时的缓存值。
Absolutely, yes. (And not just in Java, but also in C#.) There are times when you need to get or set a value that is guaranteed to be an atomic operation on your given platform, an int or boolean, for example, but do not require the overhead of thread locking. The volatile keyword allows you to ensure that when you read the value that you get the current value and not a cached value that was just made obsolete by a write on another thread.
volatile 关键字有两种不同的用法。
忙标志用于防止线程在设备忙且该标志不受锁保护时继续:
当另一个线程关闭忙标志时,测试线程将继续< /em>:
但是,由于在测试线程中频繁访问busy,因此JVM可以通过将busy的值放在寄存器中来优化测试,然后测试寄存器的内容,而不是在每次测试之前读取内存中的busy值。 测试线程永远不会看到 busy 变化,而另一个线程只会更改内存中 busy 的值,从而导致死锁。 将忙标志声明为易失性会强制在每次测试之前读取其值。
使用易失性变量可以降低内存一致性错误的风险,因为对易失性变量的任何写入都会建立一个
“发生在之前”与同一变量的后续读取的关系。 这意味着对易失性变量的更改始终对其他线程可见。
没有内存一致性错误的读取、写入技术称为原子操作。
原子动作是指一次有效地发生的动作。 原子动作不能中途停止:它要么完全发生,要么根本不发生。 在操作完成之前,原子操作的副作用是不可见的。
以下是您可以指定的原子操作:
原始变量(除 long 和 double 之外的所有类型)。
(包括长整型和双精度变量)。
干杯!
There are two different uses of volatile keyword.
A busy flag is used to prevent a thread from continuing while the device is busy and the flag is not protected by a lock:
The testing thread will continue when another thread turns off the busy flag:
However, since busy is accessed frequently in the testing thread, the JVM may optimize the test by placing the value of busy in a register, then test the contents of the register without reading the value of busy in memory before every test. The testing thread would never see busy change and the other thread would only change the value of busy in memory, resulting in deadlock. Declaring the busy flag as volatile forces its value to be read before each test.
Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a
"happens-before" relationship with subsequent reads of that same variable. This means that changes to a volatile variable are always visible to other threads.
The technique of reading, writing without memory consistency errors is called atomic action.
An atomic action is one that effectively happens all at once. An atomic action cannot stop in the middle: it either happens completely, or it doesn't happen at all. No side effects of an atomic action are visible until the action is complete.
Below are actions you can specify that are atomic:
primitive variables (all types except long and double).
(including long and double variables).
Cheers!
挥发性如下。
1> 不同线程对易失性变量的读写总是从内存中进行,而不是从线程自己的缓存或cpu寄存器中进行。 所以每个线程总是处理最新的值。
2> 当两个不同的线程使用堆中的相同实例或静态变量时,一个线程可能会认为其他线程的操作是无序的。 请参阅杰里米·曼森的博客了解这一点。 但挥发性在这里有帮助。
以下完全运行的代码显示了多个线程如何按预定义的顺序执行并打印输出,而无需使用同步关键字。
为了实现这一点,我们可以使用以下完整的运行代码。
以下 github 链接有一个自述文件,其中给出了正确的解释。
Volatile does following.
1> Read and write of volatile variables by different threads are always from memory, not from thread's own cache or cpu register. So each thread always deals with the latest value.
2> When 2 different threads work with same instance or static variables in heap, one may see other's actions as out of order. See jeremy manson's blog on this. But volatile helps here.
Following fully running code shows how a number of threads can execute in predefined order and print outputs without using synchronized keyword.
To achieve this we may use the following full fledged running code.
The following github link has a readme, which gives proper explanation.
https://github.com/sankar4git/volatile_thread_ordering
从oracle文档页面来看,需要 volatile 变量修复内存一致性问题:
这意味着对 易失性 变量的更改始终对其他线程可见。 这还意味着,当线程读取易失性变量时,它不仅会看到对易失性变量的最新更改,还会看到导致更改的代码的副作用。
正如 Peter Parker 的回答中所解释的,在没有 volatile 修饰符的情况下,每个线程的堆栈可能有自己的变量副本。 通过将变量设为
易失性
,内存一致性问题已得到解决。查看 jenkov 教程页面以更好地理解。
查看相关的 SE 问题,了解有关 volatility 和 volatile 的更多详细信息。 使用 易失性的用例:
Java 中 易失性 和同步之间的区别
一个实际用例:
您有许多线程,需要以特定格式打印当前时间,例如:
java.text.SimpleDateFormat("HH-mm-ss")
。 您可以有一个类,它将当前时间转换为 SimpleDateFormat 并每隔一秒更新一次变量。 所有其他线程都可以简单地使用此易失性变量在日志文件中打印当前时间。From oracle documentation page, the need for volatile variable arises to fix memory consistency issues:
This means that changes to a
volatile
variable are always visible to other threads. It also means that when a thread reads a volatile variable, it sees not just the latest change to thevolatile
, but also the side effects of the code that led up the change.As explained in
Peter Parker
answer, in absence ofvolatile
modifier, each thread's stack may have their own copy of variable. By making the variable asvolatile
, memory consistency issues have been fixed.Have a look at jenkov tutorial page for better understanding.
Have a look at related SE question for some more details on volatile & use cases to use volatile:
Difference between volatile and synchronized in Java
One practical use case:
You have many threads, which need to print current time in a particular format for example :
java.text.SimpleDateFormat("HH-mm-ss")
. Yon can have one class, which converts current time intoSimpleDateFormat
and updated the variable for every one second. All other threads can simply use this volatile variable to print current time in log files.如果您有一个多线程系统,并且这些多个线程处理某些共享数据,那么这些线程将在自己的缓存中加载数据。 如果我们不锁定资源,则一个线程中所做的任何更改都不会在另一个线程中可用。
通过锁定机制,我们添加对数据源的读/写访问权限。 如果一个线程修改了数据源,该数据将存储在主内存中,而不是缓存中。 当其他线程需要这些数据时,它们会从主存中读取它。 这将显着增加延迟。
为了减少延迟,我们将变量声明为
易失性
。 这意味着每当任何处理器中修改变量的值时,其他线程都将被迫读取它。 它仍然有一些延迟,但比从主内存读取要好。If you have a multithread system and these multiple threads work on some shared data, those threads will load data in their own cache. If we do not lock the resource, any change made in one thread is NOT gonna be available in another thread.
With a locking mechanism, we add read/write access to the data source. If one thread modifies the data source, that data will be stored in the main memory instead of in its cache. When others threads need this data, they will read it from the main memory. This will increase the latency dramatically.
To reduce the latency, we declare variables as
volatile
. It means that whenever the value of the variable is modified in any of the processors, the other threads will be forced to read it. It still has some delays but better than reading from the main memory.易失性变量是轻量级同步。 当需要所有线程之间最新数据的可见性并且原子性可能受到损害时,在这种情况下必须首选易失性变量。 对易失性变量的读取始终返回任何线程完成的最新写入,因为它们既没有缓存在寄存器中,也没有缓存在其他处理器看不到的缓存中。 易失性是无锁的。 当场景满足上述标准时,我使用 volatile。
Volatile Variables are light-weight synchronization. When visibility of latest data among all threads is requirement and atomicity can be compromised , in such situations Volatile Variables must be preferred. Read on volatile variables always return most recent write done by any thread since they are neither cached in registers nor in caches where other processors can not see. Volatile is Lock-Free. I use volatile, when scenario meets criteria as mentioned above.
易失性变量基本上用于在主共享缓存行更新后立即更新(刷新),以便更改立即反映到所有工作线程。
volatile variable is basically used for instant update (flush) in main shared cache line once it updated, so that changes reflected to all worker threads immediately.
下面是一个非常简单的代码,演示了对用于控制其他线程的线程执行的变量的
易失性
的要求(这是需要易失性
的一种场景)。当
易失性
未使用时:即使在“停止于:xxx<”之后,您也永远不会看到“停止于:xxx”消息/em>',程序继续运行。使用
易失性
时:您将立即看到“停止于:xxx”。演示:https://repl.it/repls/SilverAgonizingObjectcode
Below is a very simple code to demonstrate the requirement of
volatile
for variable which is used to control the Thread execution from other thread (this is one scenario wherevolatile
is required).When
volatile
is not used: you'll never see 'Stopped on: xxx' message even after 'Stopping on: xxx', and the program continues to run.When
volatile
used: you'll see the 'Stopped on: xxx' immediately.Demo: https://repl.it/repls/SilverAgonizingObjectcode
与变量一起使用时,易失性键将确保读取该变量的线程将看到相同的值。 现在,如果您有多个线程读取和写入一个变量,仅使该变量为易失性是不够的,并且数据将被损坏。 图像线程读取了相同的值,但每个线程都做了一些更改(例如增加了计数器),当写回内存时,数据完整性被破坏。 这就是为什么有必要使变量同步(可以采用不同的方式)。
如果更改由 1 个线程完成,而其他线程只需要读取该值,则 volatile 将是合适的。
The volatile key when used with a variable, will make sure that threads reading this variable will see the same value . Now if you have multiple threads reading and writing to a variable, making the variable volatile will not be enough and data will be corrupted . Image threads have read the same value but each one has done some chages (say incremented a counter) , when writing back to the memory, data integrity is violated . That is why it is necessary to make the varible synchronized (diffrent ways are possible)
If the changes are done by 1 thread and the others need just to read this value, the volatile will be suitable.
在我看来,除了停止使用 volatile 关键字的线程之外,还有两个重要的场景是
图案。 在此,单例对象需要声明为易失性。
In my opinion, two important scenarios other than stopping thread in which volatile keyword is used are:
pattern. In this the singleton object needs to be declared volatile.
是的,只要您希望多个线程访问可变变量,就必须使用 volatile。 这不是很常见的用例,因为通常您需要执行多个原子操作(例如,在修改变量之前检查变量状态),在这种情况下,您将使用同步块。
Yes, volatile must be used whenever you want a mutable variable to be accessed by multiple threads. It is not very common usecase because typically you need to perform more than a single atomic operation (e.g. check the variable state before modifying it), in which case you would use a synchronized block instead.
没有人提到long和double变量类型的读写操作的处理。 对于引用变量和大多数原始变量来说,读取和写入都是原子操作,除了 long 和 double 变量类型,它们必须使用 volatile 关键字才能成为原子操作。 @link
No one has mentioned the treatment of read and write operation for long and double variable type. Reads and writes are atomic operations for reference variables and for most primitive variables, except for long and double variable types, which must use the volatile keyword to be atomic operations. @link
假设某个线程修改了共享变量的值,前提是您没有对该变量使用
volatile
修饰符。 当其他线程想要读取该变量的值时,它们看不到更新后的值,因为它们从 CPU 的缓存而不是 RAM 内存中读取变量的值。 此问题也称为可见性问题
。通过声明共享变量
易失性
,所有对计数器变量的写入都将立即写回主内存。 此外,计数器变量的所有读取都将直接从主存储器读取。对于非易失性变量,无法保证 Java 虚拟机 (JVM) 何时将数据从主内存读取到 CPU 缓存,或将数据从 CPU 缓存写入主内存。 这可能会导致几个问题,我将在以下各节中解释这些问题。
示例:
想象一种情况,其中两个或多个线程可以访问包含如下声明的计数器变量的共享对象:
同样想象一下,只有线程 1 递增计数器变量,但线程 1 和线程 2 可能会不时读取计数器变量。
如果计数器变量未声明为 易失性,则无法保证计数器变量的值何时从 CPU 缓存写回主内存。 这意味着,CPU 缓存中的计数器变量值可能与主内存中的不同。 这种情况如下所示:
由于变量尚未被另一个线程写回到主内存,因此线程看不到变量的最新值的问题称为“可见性”问题。 一个线程的更新对其他线程不可见。
Assume that a thread modifies the value of a shared variable, if you didn't use
volatile
modifier for that variable. When other threads want to read this variable's value, they don't see the updated value because they read the variable's value from the CPU's cache instead of RAM memory. This problem also known asVisibility Problem
.By declaring the shared variable
volatile
, all writes to the counter variable will be written back to main memory immediately. Also, all reads of the counter variable will be read directly from main memory.With non-volatile variables there are no guarantees about when the Java Virtual Machine (JVM) reads data from main memory into CPU caches, or writes data from CPU caches to main memory. This can cause several problems which I will explain in the following sections.
Example:
Imagine a situation in which two or more threads have access to a shared object which contains a counter variable declared like this:
Imagine too, that only Thread 1 increments the counter variable, but both Thread 1 and Thread 2 may read the counter variable from time to time.
If the counter variable is not declared volatile there is no guarantee about when the value of the counter variable is written from the CPU cache back to main memory. This means, that the counter variable value in the CPU cache may not be the same as in main memory. This situation is illustrated here:
The problem with threads not seeing the latest value of a variable because it has not yet been written back to main memory by another thread, is called a "visibility" problem. The updates of one thread are not visible to other threads.
如果您正在开发多线程应用程序,则需要使用“易失性”关键字或“同步”以及您可能拥有的任何其他并发控制工具和技术。 此类应用程序的示例是桌面应用程序。
如果您正在开发一个将部署到应用程序服务器(Tomcat、JBoss AS、Glassfish 等)的应用程序,您不必自己处理并发控制,因为它已经由应用程序服务器处理。 事实上,如果我没记错的话,Java EE 标准禁止 servlet 和 EJB 中的任何并发控制,因为它是“基础设施”层的一部分,您应该免于处理它。 如果您要实现单例对象,则只能在此类应用程序中进行并发控制。 如果您使用像 Spring 这样的框架来编织组件,这个问题甚至已经得到解决。
因此,在大多数 Java 开发情况下(应用程序是 Web 应用程序并使用 Spring 或 EJB 等 IoC 框架),您不需要使用“易失性”。
You'll need to use 'volatile' keyword, or 'synchronized' and any other concurrency control tools and techniques you might have at your disposal if you are developing a multithreaded application. Example of such application is desktop apps.
If you are developing an application that would be deployed to application server (Tomcat, JBoss AS, Glassfish, etc) you don't have to handle concurrency control yourself as it already addressed by the application server. In fact, if I remembered correctly the Java EE standard prohibit any concurrency control in servlets and EJBs, since it is part of the 'infrastructure' layer which you supposed to be freed from handling it. You only do concurrency control in such app if you're implementing singleton objects. This even already addressed if you knit your components using frameworkd like Spring.
So, in most cases of Java development where the application is a web application and using IoC framework like Spring or EJB, you wouldn't need to use 'volatile'.
易失性仅保证所有线程(甚至线程本身)都在递增。 例如:计数器同时看到变量的同一面。 它没有用来代替同步或原子或其他东西,它完全使读取同步。 请不要将其与其他 java 关键字进行比较。 正如下面的示例所示,易失性变量操作也是原子的,它们会立即失败或成功。
即使你投入或不投入,结果也总是会有所不同。 但如果您使用 AtomicInteger,如下所示,结果将始终相同。 这与同步也是一样的。
volatile
only guarantees that all threads, even themselves, are incrementing. For example: a counter sees the same face of the variable at the same time. It is not used instead of synchronized or atomic or other stuff, it completely makes the reads synchronized. Please do not compare it with other java keywords. As the example shows below volatile variable operations are also atomic they fail or succeed at once.Even you put volatile or not results will always differ. But if you use AtomicInteger as below results will be always same. This is same with synchronized also.
每个访问易失性字段的线程都会在继续之前读取其当前值,而不是(可能)使用缓存的值。
只有成员变量可以是易失性的或瞬态的。
Every thread accessing a volatile field will read its current value before continuing, instead of (potentially) using a cached value.
Only member variable can be volatile or transient.
“... volatile 修饰符保证任何读取字段的线程都将看到最近写入的值。” - Josh Bloch
如果您正在考虑使用
易失性
,请阅读包java.util.concurrent
处理原子行为。维基百科关于单例模式的帖子显示使用中不稳定。
“… the volatile modifier guarantees that any thread that reads a field will see the most recently written value.” - Josh Bloch
If you are thinking about using
volatile
, read up on the packagejava.util.concurrent
which deals with atomic behaviour.The Wikipedia post on a Singleton Pattern shows volatile in use.
易失性
具有内存可见性的语义。 基本上,在写操作完成后,易失性字段的值对所有读取器(特别是其他线程)都可见。 如果没有易失性
,读者可能会看到一些未更新的值。回答你的问题:是的,我使用
易失性
变量来控制某些代码是否继续循环。 该循环测试易失性
值,如果为true
则继续。 可以通过调用“stop”方法将条件设置为false
。 循环在 stop 方法完成执行后测试值时看到false
并终止。我强烈推荐的书“Java Concurrency in Practice”对
volatile
给出了很好的解释。 这本书的作者是问题中引用的 IBM 文章的同一个人(事实上,他在那篇文章的底部引用了他的书)。 我对 volatile 的使用被他的文章称为“模式 1 状态标志”。如果您想了解有关如何
易失性
在幕后工作,请阅读Java 内存模型。 如果您想超越这个水平,请查看一本好的计算机架构书籍,例如 轩尼诗 Patterson 并了解缓存一致性和缓存一致性。volatile
has semantics for memory visibility. Basically, the value of avolatile
field becomes visible to all readers (other threads in particular) after a write operation completes on it. Withoutvolatile
, readers could see some non-updated value.To answer your question: Yes, I use a
volatile
variable to control whether some code continues a loop. The loop tests thevolatile
value and continues if it istrue
. The condition can be set tofalse
by calling a "stop" method. The loop seesfalse
and terminates when it tests the value after the stop method completes execution.The book "Java Concurrency in Practice," which I highly recommend, gives a good explanation of
volatile
. This book is written by the same person who wrote the IBM article that is referenced in the question (in fact, he cites his book at the bottom of that article). My use ofvolatile
is what his article calls the "pattern 1 status flag."If you want to learn more about how
volatile
works under the hood, read up on the Java memory model. If you want to go beyond that level, check out a good computer architecture book like Hennessy & Patterson and read about cache coherence and cache consistency.挥发性(vɒlətʌɪl):在常温下容易蒸发
关于
挥发性
的要点:synchronized< /code> 和
易失性
和锁。synchronized
变量。 对变量使用synchronized
关键字是非法的,并且会导致编译错误。 除了使用 Java 中的synchronized
变量,您也可以使用 javavolatile
变量,该变量将指示 JVM 线程读取volatile
变量的值来自主内存并且不在本地缓存它。来源
的使用示例>易失性
:我们在第一个请求到来时延迟创建实例。
如果我们不将
_instance
变量设置为易失性
,那么创建Singleton
实例的线程将无法与其他线程通信。 因此,如果线程 A 正在创建 Singleton 实例,并且在创建后不久,CPU 损坏等,所有其他线程将无法看到_instance
的值不为 null,并且它们会相信它仍然被分配为 null 。为什么会发生这种情况? 因为读取器线程没有执行任何锁定,并且在写入器线程退出同步块之前,内存将不会同步,并且
_instance
的值不会在主内存中更新。 使用 Java 中的 Volatile 关键字,这是由 Java 本身处理的,并且所有读取器线程都可以看到此类更新。不使用 volatile 的示例用法:
上面的代码不是线程安全的。 尽管它在同步块内再次检查实例的值(出于性能原因),但 JIT 编译器可以重新排列字节码,以便在构造函数完成执行之前设置对实例的引用。 这意味着 getInstance() 方法返回的对象可能尚未完全初始化。 为了使代码线程安全,从 Java 5 开始可以使用关键字 volatile 作为实例变量。 一旦对象的构造函数完全完成其执行,标记为易失性的变量仅对其他线程可见。
来源
易失性
在Java中的使用:快速失败迭代器通常使用列表对象上的
易失性
计数器来实现。Iterator
时,计数器的当前值将嵌入到Iterator
对象中。Iterator
操作时,该方法会比较两个计数器值,如果不同则抛出ConcurrentModificationException
。故障安全迭代器的实现通常是轻量级的。 它们通常依赖于特定列表实现的数据结构的属性。 没有一般模式。
Volatile(vɒlətʌɪl): Easily evaporated at normal temperatures
Important point about
volatile
:synchronized
andvolatile
and locks.synchronized
variable. Usingsynchronized
keyword with a variable is illegal and will result in compilation error. Instead of using thesynchronized
variable in Java, you can use the javavolatile
variable, which will instruct JVM threads to read the value ofvolatile
variable from main memory and don’t cache it locally.volatile
keyword.source
Example usage of
volatile
:We are creating instance lazily at the time the first request comes.
If we do not make the
_instance
variablevolatile
then the Thread which is creating the instance ofSingleton
is not able to communicate to the other thread. So if Thread A is creating Singleton instance and just after creation, the CPU corrupts etc, all other threads will not be able to see the value of_instance
as not null and they will believe it is still assigned null.Why does this happen? Because reader threads are not doing any locking and until the writer thread comes out of a synchronized block, the memory will not be synchronized and value of
_instance
will not be updated in main memory. With the Volatile keyword in Java, this is handled by Java itself and such updates will be visible by all reader threads.Example usage of without volatile:
The code above is not thread-safe. Although it checks the value of instance once again within the synchronized block (for performance reasons), the JIT compiler can rearrange the bytecode in a way that the reference to the instance is set before the constructor has finished its execution. This means the method getInstance() returns an object that may not have been initialized completely. To make the code thread-safe, the keyword volatile can be used since Java 5 for the instance variable. Variables that are marked as volatile get only visible to other threads once the constructor of the object has finished its execution completely.
Source
volatile
usage in Java:The fail-fast iterators are typically implemented using a
volatile
counter on the list object.Iterator
is created, the current value of the counter is embedded in theIterator
object.Iterator
operation is performed, the method compares the two counter values and throws aConcurrentModificationException
if they are different.The implementation of fail-safe iterators is typically light-weight. They typically rely on properties of the specific list implementation's data structures. There is no general pattern.
使用
易失性
的一个常见示例是使用易失性布尔
变量作为终止线程的标志。 如果您已经启动了一个线程,并且希望能够从另一个线程安全地中断它,则可以让该线程定期检查标志。 要停止它,请将标志设置为 true。 通过设置标志易失性
,您可以确保正在检查它的线程在下次检查它时会看到它已被设置,甚至无需使用synchronized
块。One common example for using
volatile
is to use avolatile boolean
variable as a flag to terminate a thread. If you've started a thread, and you want to be able to safely interrupt it from a different thread, you can have the thread periodically check a flag. To stop it, set the flag to true. By making the flagvolatile
, you can ensure that the thread that is checking it will see it has been set the next time it checks it without having to even use asynchronized
block.Java 易失性
易失性
->同步
[关于]易失性
对于程序员来说,该值始终是最新的。 问题是该值可以保存在不同类型的硬件内存上。 例如,它可以是 CPU 寄存器、CPU 缓存、RAM...CPU 寄存器和 CPU 缓存属于 CPU,不能与在多线程环境中救援的 RAM 共享数据volatile
关键字表示变量将直接从 RAM 内存读取和写入。 它通过支持
happens-before
[关于]易失性
关键字无法解决竞争条件
< super>[关于]情况使用synchronized
关键字 [关于]因此,只有当一个线程写入而其他线程仅读取
易失性
值时才安全Java Volatile
volatile
->synchronized
[About]volatile
says for a programmer that the value always will be up to date. The problem is that the value can be saved on different types of hardware memory. For example it can be CPU registers, CPU cache, RAM... СPU registers and CPU cache belong to CPU and can not share a data unlike of RAM which is on the rescue in multithreading envirompmentvolatile
keyword says that a variable will be read and written from/to RAM memory directly. It has some computation footprintJava 5
extendedvolatile
by supportinghappens-before
[About]volatile
keyword does not cure arace condition
[About] situation to sove it usesynchronized
keyword[About]As a result it safety only when one thread writes and others just read the
volatile
value易失性
对于停止线程非常有用。并不是说您应该编写自己的线程,Java 1.6 有很多不错的线程池。 但如果您确定需要一个线程,则需要知道如何停止它。
我对线程使用的模式是:
在上面的代码段中,在while循环中读取
close
的线程与调用close()
的线程不同。 如果没有 volatile,运行循环的线程可能永远不会看到要关闭的更改。请注意如何不需要同步
volatile
is very useful to stop threads.Not that you should be writing your own threads, Java 1.6 has a lot of nice thread pools. But if you are sure you need a thread, you'll need to know how to stop it.
The pattern I use for threads is:
In the above code segment, the thread reading
close
in the while loop is different from the one that callsclose()
. Without volatile, the thread running the loop may never see the change to close.Notice how there's no need for synchronization
使用 volatile 关键字声明的变量有两个使其特殊的主要品质。
如果我们有一个易失性变量,则任何线程都无法将其缓存到计算机(微处理器)的缓存中。 访问总是从主内存发生。
如果对易失性变量进行写入操作,并且突然请求读取操作 ,保证写操作会先于读操作完成。
上述两个性质推断出
另一方面,
volatile
关键字是维护具有<的共享变量的理想方法。 strong>'n' 个读取器线程,并且只有一个写入器线程 可以访问它。 一旦我们添加了 volatile 关键字,就完成了。 没有任何其他关于线程安全的开销。相反,
我们不能单独使用
volatile
关键字来满足有多个共享变量编写器线程访问它。A variable declared with
volatile
keyword, has two main qualities which make it special.If we have a volatile variable, it cannot be cached into the computer's(microprocessor) cache memory by any thread. Access always happened from main memory.
If there is a write operation going on a volatile variable, and suddenly a read operation is requested, it is guaranteed that the write operation will be finished prior to the read operation.
Two above qualities deduce that
And on the other hand,
volatile
keyword is an ideal way to maintain a shared variable which has 'n' number of reader threads and only one writer thread to access it. Once we add thevolatile
keyword, it is done. No any other overhead about thread safety.Conversly,
We can't make use of
volatile
keyword solely, to satisfy a shared variable which has more than one writer thread accessing it.