Java中什么是易失性的,何时/如何使用它们? 公共类Volatile{ 挥发性int x=0; 公共静态void main(字符串a[]{ Volatile y=新Volatile(); 试验t1=新试验(y); 试验t2=新试验(y); t1.设定名称(“A”); t2.设定名称(“B”); t1.start(); t2.start(); } } 类测试扩展了线程{ 挥发性v; 测试(挥发性v){ 这个,v=v; } @凌驾 公开募捐{ 对于(int i=0;i

Java中什么是易失性的,何时/如何使用它们? 公共类Volatile{ 挥发性int x=0; 公共静态void main(字符串a[]{ Volatile y=新Volatile(); 试验t1=新试验(y); 试验t2=新试验(y); t1.设定名称(“A”); t2.设定名称(“B”); t1.start(); t2.start(); } } 类测试扩展了线程{ 挥发性v; 测试(挥发性v){ 这个,v=v; } @凌驾 公开募捐{ 对于(int i=0;i,java,multithreading,volatile,Java,Multithreading,Volatile,输出 public class Volatile { volatile int x = 0; public static void main(String a[]) { Volatile y = new Volatile(); test t1 = new test(y); test t2 = new test(y); t1.setName("A"); t2.setName("B");

输出

public class Volatile {

    volatile int x = 0;

    public static void main(String a[]) {
        Volatile y = new Volatile();
        test t1 = new test(y);
        test t2 = new test(y);
        t1.setName("A");
        t2.setName("B");
        t1.start();
        t2.start();
    }
}

class test extends Thread {

    Volatile v;

    test(Volatile v) {
        this.v = v;
    }

    @Override
    public void run() {
        for (int i = 0; i < 4; i++) {
            System.out.println(Thread.currentThread().getName() + "Says Before " + v.x);
            v.x++;
            System.out.println(Thread.currentThread().getName() + "Says After " + v.x);
        }
    }
}
最早在0之前
B在0之前到达
下午2点以后
B在2点之前睡觉
3点后休息

在1之后尽快标记变量volatile将阻止JVM缓存该值,但它不会为您解决同步问题(例如在修改变量和打印变量之间交换线程)

例如,线程A在0之前输出
,然后被调出,以便线程B运行。该值仍然为零,因为A尚未更新它。然后B更新它,然后A返回并更新它,然后打印它。这意味着您可能最终会遇到以下情况:

ASays Before 0
BSays Before 0
BSays After 2
BSays Before 2
BSays After 3
ASays After 1   <--- Is it a cache value ?
BSays Before 3
ASays Before 3
BSays After 4
BSays Before 5
BSays After 6
ASays After 5    <--- Is it a cache value ?
ASays Before 6
ASays After 7
ASays Before 7
ASays After 8
ASays Before 0
BSays Before 0
ASays After 2
这不太理想

此外,
println
本身不是原子的,因此它可能会在中途中断,导致线程持有过时的打印值(即,稍后在输出流中显示的理想值)

要正确更新和打印,应在需要原子化使用变量的块周围使用
synchronized
,例如:

ASays Before 0
BSays Before 0
BSays After 2
BSays Before 2
BSays After 3
ASays After 1   <--- Is it a cache value ?
BSays Before 3
ASays Before 3
BSays After 4
BSays Before 5
BSays After 6
ASays After 5    <--- Is it a cache value ?
ASays Before 6
ASays After 7
ASays Before 7
ASays After 8
ASays Before 0
BSays Before 0
ASays After 2
但是,在您的特定情况下,您不能使用
this
,因为它是thread对象,这意味着其中有两个对象,因此它们不会根据需要相互阻塞

您可以通过在thread类中引入一个静态对象来解决这个问题:

synchronized (this) {
    System.out.println(Thread.currentThread().getName() + " before " + v.x);
    v.x++;
    System.out.println(Thread.currentThread().getName() + " after " + v.x);
}
并将其用于同步:

static Object o = new Object();

你看过吗?@MadProgrammer它说对一个可变变量的更改对其他线程总是可见的,这是真的,但问题仍然是这些更改实际上是什么时候提交的…@MadProgrammer你的意思是,我们永远不会确定增量是否完成。我说得对吗?如果是,那么我们永远无法预测多线程环境中的可变值。对吗?链接的答案对此有一些有趣的观点,因此您可以同时使用
同步
volatile
,或者使用
原子
API,它为您提供了一些其他选项请纠正我,那么我为什么要使用volatile?如果我将同步,那么我可以使用简单的非易失性变量。这将产生同样的结果。我说的对吗?另外,我做了更改,正如你所说的,我仍然看到相同的值模式。@jWeaver
synchronized
将保护数据不被修改,但如果数据被缓存,就不会了,因为易失性会阻止值被缓存,并让你实时访问,这是一种平衡行为。最近,我使用了
Atomc
类而不是volatile类,我倾向于选择同步操作,因为有很多情况下volatile没有帮助。举个例子,当您想要一个一致的print modify print值时,这是不好的。事实上,我似乎还记得,基于变量的旧值修改变量是不合适的,因为Java不能保证
x++
始终是原子的。@jWeaver对此表示歉意,事实证明
在您的例子中,这个
是两个不同的线程对象之一,因此它们不会同步。修改了答案来解决这个问题。