volatile
关键字。由于不是很熟悉它,我找到了这篇说明文章。鉴于该文章详细解释了关键字的相关内容,您是否曾经使用过它,或者是否可以想象出可以正确使用该关键字的情况?
volatile
关键字。由于不是很熟悉它,我找到了这篇说明文章。这意味着对于使用volatile变量可以降低内存一致性错误的风险,因为对于任何一个volatile变量的写入都会与随后对该变量的读取建立happens-before关系。
volatile
变量的更改始终可见于其他线程。它还意味着当一个线程读取一个volatile
变量时,它不仅能看到最新的volatile
变化,还能看到导致变化的代码的副作用。volatile
修饰符的情况下,每个线程的堆栈可能有自己的变量副本。通过将变量设置为volatile
,已经解决了内存一致性问题。一个实际应用场景:
你有许多线程,需要以特定格式打印当前时间,例如:java.text.SimpleDateFormat("HH-mm-ss")
。你可以有一个类,将当前时间转换为SimpleDateFormat
并每秒更新一次变量。所有其他线程可以简单地使用这个volatile
变量来在日志文件中打印当前时间。
volatile变量基本上用于在更新后立即在主共享缓存行中进行更新(刷新),以便更改立即反映到所有工作线程中。
易变变量是轻量级同步。当所有线程之间的最新数据的可见性是必需的,而原子性可以被牺牲时,在这种情况下,应优先选择易变变量。由于易变变量既不缓存在寄存器中,也不缓存在其他处理器无法看到的缓存中,因此读取易变变量始终返回任何线程执行的最新写入。易变变量是无锁的。当场景符合上述标准时,我使用易变变量。
volatile
的一种情况),volatile
的要求。// Code to prove importance of 'volatile' when state of one thread is being mutated from another thread.
// Try running this class with and without 'volatile' for 'state' property of Task class.
public class VolatileTest {
public static void main(String[] a) throws Exception {
Task task = new Task();
new Thread(task).start();
Thread.sleep(500);
long stoppedOn = System.nanoTime();
task.stop(); // -----> do this to stop the thread
System.out.println("Stopping on: " + stoppedOn);
}
}
class Task implements Runnable {
// Try running with and without 'volatile' here
private volatile boolean state = true;
private int i = 0;
public void stop() {
state = false;
}
@Override
public void run() {
while(state) {
i++;
}
System.out.println(i + "> Stopped on: " + System.nanoTime());
}
}
当没有使用 volatile
时: 即使在 'Stopping on: xxx' 后,您永远不会看到 'Stopped on: xxx' 的消息,并且程序将继续运行。
Stopping on: 1895303906650500
使用 volatile
关键字:你会立即看到 'Stopped on: xxx'。
Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300
当使用变量时,volatile关键字可以确保读取该变量的线程看到相同的值。现在,如果有多个线程读取和写入一个变量,使变量成为volatile是不够的,数据将会被破坏。假设线程已经读取了相同的值,但每个线程都进行了一些更改(例如增加计数器),当写回内存时,数据完整性就会被破坏。这就是为什么需要使变量同步(有不同的方法)。
如果更改是由1个线程完成的,而其他线程只需要读取此值,则volatile是合适的。