繁体   English   中英

多线程中的Java volatile关键字

[英]Java volatile keyword in multithreading

我试图在Java多线程中重现非易失性变量的行为。 在这里,我在OccurrenceCounter.java类中进行了非易失性变量test ThreadDemo.java类中,我具有生成两个线程的main方法。 thread1它在while循环中连续检查非易失性变量test的值。 如果我在MyRunnableThread1.java中的以下示例中运行,则不会从CPU高速缓存中获取encesCounter.isTest occurrenceCounter.isTest()的值。

OccurrenceCounter.java:

public class OccurrenceCounter {

    // non-volatile variable
    private   boolean test=true;

    public OccurrenceCounter(boolean test)
    {
        this.test=test;
    } 

    public boolean isTest() {
        return test;
    }

    public void setTest(boolean test) {
        this.test = test;
    }
}

MyRunnableThread1.java:

// Thread 1
public class MyRunnableThread1 implements Runnable {

    private String threadName;
    private OccurrenceCounter occurrenceCounter;

    public MyRunnableThread1(String threadName,OccurrenceCounter occurrenceCounter)
    {
        this.threadName=threadName; 
        this.occurrenceCounter=occurrenceCounter;
    }

    @Override
    public void run() {

        System.out.println("Thread "+threadName + " started");
        System.out.println("value of flag:"+occurrenceCounter.isTest());

        int i=0;
        // random processing code 
        while(i<1000000000L)
        {
           i++; 
           i++;
           i++;
        }
        // checking whether non-volatile variable is taken from cpu cache
        while(occurrenceCounter.isTest())
        {

        }
    System.out.println("Thread "+threadName + " finished");
    }
}

MyRunnableThread2.java:

// Thread 2
public class MyRunnableThread2 implements Runnable {

    private String threadName;
    private OccurrenceCounter occurrenceCounter;

    public MyRunnableThread2(String threadName,OccurrenceCounter occurrenceCounter)
    {
        this.threadName=threadName; 
        this.occurrenceCounter=occurrenceCounter;
    }

    @Override
    public   void run() {

        System.out.println("Thread "+threadName + " started");

        occurrenceCounter.setTest(false);
        System.out.println("Thread "+threadName + " finished");
    }
}

ThreadDemo.java:

public class ThreadDemo {

    public static void main(final String[] arguments) throws InterruptedException {

       System.out.println("main thread started");

       OccurrenceCounter occurrenceCounter =new OccurrenceCounter(true);

       MyRunnableThread1 myRunnableThread1=new MyRunnableThread1("Thread1", occurrenceCounter);
       MyRunnableThread2 myRunnableThread2=new MyRunnableThread2("Thread2", occurrenceCounter);

       Thread t1=new Thread(myRunnableThread1);
       Thread t2=new Thread(myRunnableThread2);

       t1.start();

       try
       {
           Thread.sleep(100);
       }
       catch(Exception e)
       {
           System.out.println("main thread sleep exception:"+e);
       }

       t2.start();

       System.out.println("main thread finished");
   }
}

在这里,在MyRunnableThread1.java中的while循环中,即使OcuurenceCounter类中的test变量是非易失性的,也不会从CPU缓存中返回条件enceingCounter.isTest occurrenceCounter.isTest() 但是,如果我删除第一个while循环:

while(i<1000000000L)
{
   i++; 
   i++;
   i++;
}

然后,我可以看到,即使条件thread2更新了thread2并且thread1从未终止,条件thread1 occurrenceCounter.isTest()始终为false。 那么为什么这个while循环:

while(i<1000000000L)
{
   i++; 
   i++;
   i++;
}

影响非易失性变量的行为? 这是第一次同时强制从内存而不是从thread1 CPU缓存中读取值的while循环吗? 我试图得到很多答案。 但是我不能。

请任何人帮我解决这个问题。

使用volatile ,JMM可以确保更新的值可用于其他线程。

如果没有volatile或其他同步,则更新的值可能会或无法用于其他线程,这是不确定的。

在这种情况下,循环

while(i<1000000000L) {
    i++; 
    i++;
    i++;
}

无法保证变量test内存可见性,这只是巧合。 不要依赖它。

volatile关键字可确保如果一个Thread对该变量进行了某些更改,那么在更改后读取该变量的其他Thread将看到该变量。 如果使用普通的非易失volatile变量,则其他Threads可能会也可能不会看到它。 它取决于JVM的内部。

那么,如何解决呢? 有几种方法:

  1. 如前所述,您可以使其变得volatile 这可能是最简单的方法:

     public class OccurrenceCounter { private volatile boolean test=true; // ... } 
  2. 您可以使访问器synchornized 在这种情况下,您必须同步所有变量访问:

     public class OccurrenceCounter { private boolean test=true; public OccurrenceCounter(boolean test) { this.test=test; } public synchronized boolean isTest() { return test; } public synchronized void setTest(boolean test) { this.test = test; } } 
  3. 您可以使用AtomicBoolean来为您处理所有这些。 您为此付出的代价是该API会更加冗长。 除非您想使用compareAndSet()getAndSet()方法,否则这可能是一个getAndSet()

     private AtomicBoolean test = new AtomicBoolean(true); public OccurrenceCounter(boolean test) { this.test.set(test); } public boolean isTest() { return test.get(); } public void setTest(boolean test) { this.test.set(test); } 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM