今天工作时,我在Java中遇到了volatile关键字。不是很熟悉,我找到了这个解释。
鉴于那篇文章详细解释了所讨论的关键字,您曾经使用过它吗?或者您曾经看到过以正确方式使用该关键字的情况吗?
今天工作时,我在Java中遇到了volatile关键字。不是很熟悉,我找到了这个解释。
鉴于那篇文章详细解释了所讨论的关键字,您曾经使用过它吗?或者您曾经看到过以正确方式使用该关键字的情况吗?
当前回答
Volatile变量是轻量级同步。当所有线程之间的最新数据可见性是必需的,并且原子性可能会受到损害时,在这种情况下,Volatile变量必须是首选。对volatile变量的读取总是返回任何线程最近完成的写操作,因为它们既不缓存在寄存器中,也不缓存在其他处理器看不到的缓存中。Volatile是无锁的。当场景满足上面提到的条件时,我使用volatile。
其他回答
每个访问volatile字段的线程在继续之前都会读取它的当前值,而不是(可能)使用缓存的值。
只有成员变量可以是易变的或瞬变的。
是的,当你想要一个可变变量被多个线程访问时,必须使用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)
如果修改是由一个线程完成的,而其他线程只需要读取这个值,则volatile将是合适的。
虽然我在这里提到的答案中看到了许多很好的理论解释,但我在这里添加了一个实际的例子来解释:
1.
代码在不使用volatile的情况下运行
public class VisibilityDemonstration {
private static int sCount = 0;
public static void main(String[] args) {
new Consumer().start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
return;
}
new Producer().start();
}
static class Consumer extends Thread {
@Override
public void run() {
int localValue = -1;
while (true) {
if (localValue != sCount) {
System.out.println("Consumer: detected count change " + sCount);
localValue = sCount;
}
if (sCount >= 5) {
break;
}
}
System.out.println("Consumer: terminating");
}
}
static class Producer extends Thread {
@Override
public void run() {
while (sCount < 5) {
int localValue = sCount;
localValue++;
System.out.println("Producer: incrementing count to " + localValue);
sCount = localValue;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return;
}
}
System.out.println("Producer: terminating");
}
}
}
在上面的代码中,有两个线程——生产者和消费者。
生产者线程在循环中迭代5次(睡眠时间为1000毫秒或1秒)。在每次迭代中,生产者线程将sCount变量的值增加1。因此,在所有迭代中,生产者将sCount的值从0更改为5
使用者线程处于一个常量循环中,每当sCount的值发生变化时,它就会打印,直到值达到5为止。
两个循环同时开始。因此,生产者和消费者都应该将sCount的值打印5次。
输出
Consumer: detected count change 0
Producer: incrementing count to 1
Producer: incrementing count to 2
Producer: incrementing count to 3
Producer: incrementing count to 4
Producer: incrementing count to 5
Producer: terminating
分析
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.
代码使用volatile运行
在上面的代码中,用下面的代码替换声明了sCount的代码行:
private volatile static int sCount = 0;
输出
Consumer: detected count change 0
Producer: incrementing count to 1
Consumer: detected count change 1
Producer: incrementing count to 2
Consumer: detected count change 2
Producer: incrementing count to 3
Consumer: detected count change 3
Producer: incrementing count to 4
Consumer: detected count change 4
Producer: incrementing count to 5
Consumer: detected count change 5
Consumer: terminating
Producer: terminating
分析
当我们声明一个变量为volatile时,这意味着所有对这个变量的读写操作都将直接进入主存。这些变量的值永远不会被缓存。
由于sCount变量的值永远不会被任何线程缓存,消费者总是从主内存中读取sCount的原始值(在那里由生产者线程更新)。因此,在这种情况下,输出是正确的,两个线程都打印了5次不同的sCount值。
通过这种方式,volatile关键字解决了可见性问题。
挥发性
volatile -> synchronized[关于]
Volatile表示对于程序员来说,该值总是最新的。问题是该值可以保存在不同类型的硬件内存中。例如,它可以是CPU寄存器,CPU缓存,RAM…СPU寄存器和CPU缓存属于CPU,不能共享数据,不像RAM在多线程环境中处于抢救状态
volatile关键字表示变量将直接从/写入RAM内存。它有一些计算足迹
Java 5通过支持happens-before扩展volatile[关于]
对volatile字段的写入发生在后续每次读取该字段之前。
Read is after write
volatile关键字不能修复竞态条件[关于],使用synchronized关键字[关于]
因此,只有当一个线程写入,而其他线程只是读取volatile值时才安全