易失性关键字无法按预期工作

时间:2014-06-19 17:35:24

标签: java multithreading volatile

我正在学习易变量。我知道volatile的作用,我为Volatile变量编写了一个示例程序,但没有按预期工作。

为什么程序进入无限循环? 如果变量“isTrue”是volatile,那么它应该总是从主内存中获取值?线程为什么要缓存它?

有人可以解释一下原因吗?如果可以提供解决方案...(我不会将 isTrue 放入while循环

我有一个VolatileSample类: -

    public class VolatileSample{

static volatile boolean isTrue=true;

public VolatileSample(boolean is){
    isTrue=is;
}

public void print() {
    boolean b=isTrue;
    while (b) {
        System.out.println("In loop!");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

public void setFalse() {
    boolean b=false;
    System.out.println("Setting value as false");
    isTrue=b;
}

}

创建了两个主题: -

   public class Thread1 extends Thread{

VolatileSample sample;
public Thread1(VolatileSample sample){
    this.sample=sample;
}
public void run(){
    sample.print();
}

} 和

   public class Thread2 extends Thread{

VolatileSample sample;

public Thread2(VolatileSample sample){
    this.sample=sample;
}
public void run(){
    sample.setFalse();
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

}

主要课程: -

   public class Test {  
public static void main(String[] args) throws InterruptedException {
    // TODO Auto-generated method stub
    VolatileSample sample=new VolatileSample(true);

    Thread1 t1=new Thread1(sample);
    Thread2 t2=new Thread2(sample);

    t1.start();
    t2.start();
}

}

3 个答案:

答案 0 :(得分:4)

当您使用while(b)时,您在print方法中使用while(isTrue)b是一个未更新的局部变量,而isTrue是可以在外部更新的volatile变量。这应该有效:

//using this you will understand meaning of volatile
while (isTrue) {
    System.out.println("In loop!");
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

请注意Java is pass by value, not by reference。更新isTrue字段的值不会直接更新b方法中print本地参数的值。如果您不想使用while(isTrue)(非常奇怪),那么只需在循环中更新b的值:

while (b) {
    System.out.println("In loop!");
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    //you need to use the volatile variable in order to understand volatile
    b = isTrue;
}

请注意,如果您从未将isTrue更新为false,则循环将是无限的。让main方法在天真延迟后更新字段:

public class Test {

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        VolatileSample sample=new VolatileSample(true);

        Thread1 t1=new Thread1(sample);
        Thread2 t2=new Thread2(sample);

        t1.start();
        t2.start();
        //naive delay
        Thread.sleep(2500);
        sample.setFalse();
    }
}

您说您不想在isTrue循环中使用while变量。然后,在获取其值后,您无法在b方法内更新print变量。由于此代码中的b将为true,因此您将拥有无限循环。周期。

似乎您希望通过两个线程同步此类,但这不是volatile字段的重点,因为您对方法或代码段使用synchronized修饰符,如@JigarJoshi指出。通过阅读Simplest and understandable example of volatile keyword in java

,您可以更好地理解

答案 1 :(得分:1)

您实际上是将false设置为本地变量

public void setFalse() {
    boolean b=false; // <-- this is local variable
    System.out.println("Setting value as false");
    isTrue=b; // <-- this is shared but you don't check on it in your while loop
}

不是共享的

您可能希望通过选中isTrue

来迭代while循环

答案 2 :(得分:0)

@LuiggiMendoza是对的:在学习语言的基本功能(例如方法调用如何工作)之前,不应该尝试理解volatile

但是如果你坚持......尝试运行它,然后在从volatile声明中取出timeToStop关键字后再试一次。我无法保证您的操作系统上的JVM会发生什么; Java语言规范保证当您volatile时会发生什么。但是,在我的操作系统上的JVM中,如果变量不是volatile,程序永远不会终止。

class Foo {
    private static volatile boolean timeToStop = false;
    private static long count = 0;

    public static void main(String[] argsIgnored) throws InterruptedException {
        Thread busyThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (! timeToStop) {
                    count += 1;
                }
            }
        });
        busyThread.start();
        Thread.sleep(1000);
        timeToStop = true;
        busyThread.join();
        System.out.println(count);
    }
}