如何利用Volatile在JUC中保障并发编程的可见性?
2023-09-19 04:17:14
共享变量的可见性:深入了解 Volatile
在并行编程的世界中,协调多个线程同时访问共享变量至关重要。Volatile 是 Java 中一个非常重要的,它确保了共享变量在多线程环境中的可见性。然而,它并不是一个万能的解决方案,理解它的局限性同样重要。
Volatile 的可见性
Volatile 保证当一个线程修改共享变量时,其他线程能够立即看到该修改。这确保了共享变量始终反映其最新状态。例如,考虑以下代码段:
public class VolatileDemo {
private static volatile int count = 0;
public static void main(String[] args) {
new Thread(() -> {
count = 1;
}).start();
while (count == 0) {
// 这里会一直循环,因为 count 的值不会立即更新
}
System.out.println("count: " + count);
}
}
如果没有使用 Volatile 关键字,则第二个线程可能无法立即看到 count 的修改,因为第二个线程可能仍在使用它本地内存中的 count 值。但是,由于使用了 Volatile 关键字,第一个线程修改 count 的值后,第二个线程将立即看到该修改,因为 Volatile 关键字会强制编译器和处理器将对 count 的修改操作直接写入到主内存中。
Volatile 的非原子性
Volatile 不保证对共享变量的修改是原子的。原子性意味着一个操作要么全部完成,要么根本不完成,不会出现中间状态。考虑以下代码段:
public class VolatileDemo2 {
private static volatile int count = 0;
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
count++;
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count: " + count);
}
}
在这种情况下,我们希望 count 的最终值为 1000。但是,由于 Volatile 不能保证原子性,因此 count 的值可能小于 1000,这是因为当一个线程正在修改 count 的值时,另一个线程也可能正在修改 count 的值,导致 count 的值被覆盖。
Volatile 的有序性
Volatile 保证对共享变量的修改操作是按顺序执行的。例如,考虑以下代码段:
public class VolatileDemo3 {
private static volatile int count = 0;
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
count++;
System.out.println("count: " + count);
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count: " + count);
}
}
由于使用了 Volatile 关键字,count 的最终值为 1000,并且在控制台输出的 count 的值也是按顺序递增的。
Volatile 的使用场景
Volatile 主要用于以下场景:
- 保证共享变量的可见性
- 通过使用原子变量类保证共享变量的原子性
- 通过使用 Lock 锁保证共享变量的有序性
结论
Volatile 是一个非常重要的关键字,它可以在多线程环境中协调共享变量。它保证了共享变量的可见性,但不能保证原子性和有序性。理解 Volatile 的特性对于有效利用它至关重要。
常见问题解答
-
Volatile 与 synchronized 的区别是什么?
Volatile 保证共享变量的可见性,但不保证原子性或有序性。synchronized 保证共享变量的原子性和有序性,但也带来了开销。
-
为什么 Volatile 无法保证原子性?
Volatile 允许多个线程同时访问共享变量,这可能会导致并发修改,从而破坏原子性。
-
如何在 Volatile 中实现原子性?
可以使用原子变量类,例如 AtomicInteger,它们通过内部锁定机制来保证原子性。
-
如何在 Volatile 中实现有序性?
可以使用 Lock 锁,它允许一个线程一次访问共享变量,从而保证有序性。
-
Volatile 的常见用法有哪些?
Volatile 通常用于需要共享变量快速可见性的情况下,例如标志变量或计数器。