Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java中volatile关键字的最简单易懂示例_Java_Multithreading_Concurrency_Volatile - Fatal编程技术网

Java中volatile关键字的最简单易懂示例

Java中volatile关键字的最简单易懂示例,java,multithreading,concurrency,volatile,Java,Multithreading,Concurrency,Volatile,我正在阅读Java中的volatile关键字,完全理解其中的理论部分 但是,我要搜索的是一个很好的例子,它显示了若变量不是易变的,若它是易变的,会发生什么 以下代码段未按预期工作(取自): 理想情况下,如果keepRunning不是易失性的,线程应该无限期地继续运行。但是,几秒钟后它确实停止了 我有两个基本问题: 有人能举例说明吗?不符合JLS的理论 volatile是同步的替代品吗?它实现原子性了吗 理想情况下,若keepRunning不是易变的,那个么线程应该无限期地继续运行。但是,几秒

我正在阅读Java中的volatile关键字,完全理解其中的理论部分

但是,我要搜索的是一个很好的例子,它显示了若变量不是易变的,若它是易变的,会发生什么

以下代码段未按预期工作(取自):

理想情况下,如果
keepRunning
不是易失性的,线程应该无限期地继续运行。但是,几秒钟后它确实停止了

我有两个基本问题:

  • 有人能举例说明吗?不符合JLS的理论
  • volatile是同步的替代品吗?它实现原子性了吗
理想情况下,若keepRunning不是易变的,那个么线程应该无限期地继续运行。但是,几秒钟后它确实停止了

如果在单处理器中运行,或者系统非常繁忙,则操作系统可能会调出线程,从而导致某些级别的缓存失效。没有
volatile
并不意味着内存不会被共享,但是JVM正试图不同步内存(如果可能的话),因为性能原因,所以内存可能不会被更新

另一件需要注意的事情是,
System.out.println(…)
是同步的,因为底层的
PrintStream
执行同步以停止重叠输出。因此,您可以在主线程中“免费”获得内存同步。然而,这仍然不能解释为什么阅读循环会看到更新

无论
println(…)
行是输入还是输出,您的程序都会在带有Intel i7的MacBook Pro上的Java6下为我旋转

有人能举例说明吗?不符合JLS的理论

我认为你的例子很好。不确定为什么它不能与所有已删除的
System.out.println(…)
语句一起工作。它对我有用

volatile是同步的替代品吗?它实现原子性了吗

就内存同步而言,
volatile
抛出的内存屏障与
synchronized
块相同,只是
volatile
屏障是单向的,而不是双向的<代码>易失性读取会抛出负载屏障,而写入会抛出存储屏障。
synchronized
块是一个双向屏障,添加了互斥锁


然而,就原子性而言,答案是“视情况而定”。如果正在从字段读取或写入值,则
volatile
提供了适当的原子性。但是,递增
volatile
字段会受到限制,即
++
实际上是3个操作:读取、递增、写入。在这种情况下或更复杂的互斥情况下,可能需要一个完整的
synchronized
AtomicInteger
通过复杂的测试和设置自旋循环解决了
++
问题。

volatile
不一定会产生巨大的变化,这取决于JVM和编译器。然而,对于许多(边缘)情况,可能是优化导致变量的更改未被注意到与正确写入变量之间的差异

基本上,优化器可以选择将非易失性变量放在寄存器或堆栈上。如果另一个线程在堆或类的原语中更改它们,那么另一个线程将继续在堆栈中查找它们,并且它将过时


volatile
确保不会发生此类优化,所有读写操作都直接到堆或其他所有线程都可以看到它的地方。

当变量是
volatile
时,它保证不会缓存它,并且不同的线程将看到更新的值。但是,不将其标记为volatile并不保证相反<代码>易失性是JVM中被破坏了很长一段时间的东西之一,但始终没有得到很好的理解。

易失性-->保证了可见性而不是原子性

同步(锁定)-->保证可见性和原子性(如果正确完成)

Volatile不能替代同步

仅在更新引用且不对其执行某些其他操作时才使用volatile

例如:

volatile int i = 0;

public void incrementI(){
   i++;
}
如果不使用同步或AtomicInteger,将不会是线程安全的,因为递增是一个复合操作

为什么程序不能无限期运行

这取决于各种情况。在大多数情况下,JVM足够智能,可以刷新内容

讨论volatile的各种可能用途。正确使用volatile是很棘手的,我会说“如果有疑问,请忽略它”,使用synchronized block代替

此外:


同步块可以用来代替volatile,但反过来就不正确了

对于您的特定示例:如果未声明为volatile,服务器JVM可以将
keepRunning
变量从循环中提升出来,因为它在循环中未被修改(将其转换为无限循环),但客户端JVM不会。这就是为什么你会看到不同的结果

关于可变变量的一般解释如下:

当一个字段被声明为volatile时,编译器和运行时会注意到该变量是共享的,并且不应使用其他内存操作对其进行重新排序。易失性变量不会缓存在寄存器或缓存中,在这些寄存器或缓存中,易失性变量对其他处理器是隐藏的,因此对易失性变量的读取总是返回任何线程最近的写入

易失性变量的可见性影响超出了易失性变量本身的值。当线程A写入易失性变量,然后线程B读取该变量时,在写入易失性变量之前A可见的所有变量的值都会
volatile int i = 0;

public void incrementI(){
   i++;
}
  volatile boolean flag;
  while (!flag)  {
     // do something untill flag is true
  }
class TestVolatile extends Thread{
    //volatile
    boolean keepRunning = true;

    public void run() {
        long count=0;
        while (keepRunning) {
            count++;
        }

        System.out.println("Thread terminated." + count);
    }

    public static void main(String[] args) throws InterruptedException {
        TestVolatile t = new TestVolatile();
        t.start();
        Thread.sleep(1000);
        System.out.println("after sleeping in main");
        t.keepRunning = false;
        t.join();
        System.out.println("keepRunning set to " + t.keepRunning);
    }
}
public class VolatileDemo {

    private static volatile int MY_INT = 0;

    public static void main(String[] args) {

        ChangeMaker changeMaker = new ChangeMaker();
        changeMaker.start();

        ChangeListener changeListener = new ChangeListener();
        changeListener.start();

    }

    static class ChangeMaker extends Thread {

        @Override
        public void run() {
            while (MY_INT < 5){
                System.out.println("Incrementing MY_INT "+ ++MY_INT);
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException exception) {
                    exception.printStackTrace();
                }
            }
        }
    }

    static class ChangeListener extends Thread {

        int local_value = MY_INT;

        @Override
        public void run() {
            while ( MY_INT < 5){
                if( local_value!= MY_INT){
                    System.out.println("Got Change for MY_INT "+ MY_INT);
                    local_value = MY_INT;
                }
            }
        }
    }

}
class MyThread extends Thread {
    private boolean running = true;   //non-volatile keyword

    public void run() {
        while (running) {
            System.out.println("hello");
        }
    }

    public void shutdown() {
        running = false;
    }
}

public class Main {

    public static void main(String[] args) {
        MyThread obj = new MyThread();
        obj.start();

        Scanner input = new Scanner(System.in);
        input.nextLine(); 
        obj.shutdown();   
    }    
}
private volatile boolean running = true;  //volatile keyword
Access_Modifier volatile DataType Variable_Name;
    +--------------+--------+-------------------------------------+
    |  Flag Name   |  Value | Interpretation                      |
    +--------------+--------+-------------------------------------+
    | ACC_VOLATILE | 0x0040 | Declared volatile; cannot be cached.|
    +--------------+--------+-------------------------------------+
    |ACC_TRANSIENT | 0x0080 | Declared transient; not written or  |
    |              |        | read by a persistent object manager.|
    +--------------+--------+-------------------------------------+
public class VolatileTest {
    private static final Logger LOGGER = MyLoggerFactory.getSimplestLogger();

    private static volatile int MY_INT = 0;

    public static void main(String[] args) {
        new ChangeListener().start();
        new ChangeMaker().start();
    }

    static class ChangeListener extends Thread {
        @Override
        public void run() {
            int local_value = MY_INT;
            while ( local_value < 5){
                if( local_value!= MY_INT){
                    LOGGER.log(Level.INFO,"Got Change for MY_INT : {0}", MY_INT);
                     local_value= MY_INT;
                }
            }
        }
    }

    static class ChangeMaker extends Thread{
        @Override
        public void run() {

            int local_value = MY_INT;
            while (MY_INT <5){
                LOGGER.log(Level.INFO, "Incrementing MY_INT to {0}", local_value+1);
                MY_INT = ++local_value;
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) { e.printStackTrace(); }
            }
        }
    }
}
public class VolatileDemo {
    static class Processor {
        //without volatile program keeps running on my platform
        private boolean flag = false;

        public void setFlag() {
            System.out.println("setting flag true");
            this.flag = true;
        }

        public void process() {
            while(!flag) {
                int x = 5;
                // using sleep or sout will end the program without volatile.
                // Probably these operations, cause thread to be rescheduled, read from memory. Thus read new flag value and end.
            }

            System.out.println("Ending");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Processor processor = new Processor();
        Thread t1 = new Thread(processor::process);

        t1.start();

        Thread.sleep(2000);
        processor.setFlag();

    }
}