乐趣区

关于后端:带你理解volatile关键字

回顾之前 Java 内存模型特色能够理解到该模型是围绕着并发过程中如何解决原子性、可见性和有序性这三个特色来建设的。

原子性:一个操作或多个操作要么全副执行实现且执行过程不被中断,要么就不执行。Java 内存模型来间接保障的原子性变量操作包含 read、load、assign、use、store 和 write 这六个,如果利用场景须要更大范畴的原子性保障,Java 内存模型还提供了 lock 和 unlock 操作来满足需要,比方 synchronize 关键字。在 synchronize 块之间的操作就具备原子性。

可见性:指当一个线程批改了共享变量的值,其它线程可能立刻得悉这个批改。Java 内存模型是通过在变量批改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的形式来实现可见性的。

有序性:在本线程内,所有操作都是有序的,即依照代码先后顺序执行;如果在一个线程察看另一个线程,所有操作都是无序的,因为有“指令重排序”景象和“工作内存与主内存同步提早”景象。

理解上述并发解决三大个性之后,再看 volatile 关键字,该关键字满足可见性和有序性,所以在 Java 中能提供最轻量级的同步机制。

volatile 可见性:volatile 的规定保障了新值能立刻同步到主内存,以及每次应用前立刻从主内存刷新。因而 volatile 保障了多线程操作变量的可见性。

《2020 最新 Java 根底精讲视频教程和学习路线!》
volatile 有序性:应用 volatile 润饰的变量会禁止指令重排序,JVM 中对不是原子性的操作会进行重排序的优化,只有不影响最终计算结果。 例如:

// 线程 1 中

{

    ... 

    obj= getObject();      // 步骤 1

    isRegister = true;     // 步骤 2

    ...

}

 

// 线程 2 中

{

    ...

 if (isRegister) {     // 步骤 3,依赖步骤 2 的值

        fun(obj);         // 步骤 4,依赖步骤 1 的值

    }

    ...

} 

上述代码中,线程 1 中的代码步骤 1 和步骤 2 可能呈现重排序的状况,因为对线程 1 来说程序打乱不影响线程 1 本人的运算后果,然而对线程 2 来说,如果线程 1 中的步骤 2 先执行,最终就无奈失去正确的后果。

volatile 润饰的变量,在读取或者写入的前后都会插入内存屏障来达到禁止重排序的成果,进而保障有序性。

volatile 不能保障原子性,因而不能齐全达到线程平安成果,除非满足一下条件:

  • 运算后果不依赖以后值,或者可能确保只有繁多的线程批改该变量的值。
  • 变量不须要与其余状态变量独特参加不变束缚。

比方 volatile 润饰的变量 count 呈现 count++、count+ 1 等非原子操作,就无奈确保线程平安。

import java.io.*;

class test 

{

 private static final int THREAD_COUNT = 20;

 public static volatile int race = 0;

 public static void increase () {race++;}

 public static void main (String[] args) throws java.lang.Exception

    {System.out.println("hi");

 for (int i = 0; i < THREAD_COUNT; i++) {new Thread(new Runnable() {

 @Override

 public void run() {for (int j = 0; j < 1000; j++) {increase();

                    }

                }

            }).start();}

 while (Thread.activeCount() > 1) {Thread.yield();

        }

        System.out.println("race ="+ race);

    }

}

运行后果:

hi
race = 19902

留神,测试的后果跟环境无关,有的测试环境可能后果呈现正确的状况,能够将测试数据改大一点。自己测试时,开始抉择 10 个线程,而后只累加 10 次,测试下来发现后果都是正确的~

另外大家可能会纳闷 volatile 不是保障变量的可见性了吗?一旦被批改会立刻同步到主内存中,确保其它线程都拿到最新数据。这里能够这样了解,比方线程 1 和线程 2 都拿到最新 volatile 润饰的变量 count = 10, 当 count++ 时,因为不是原子操作,当线程 1 还在执行加加指令时,线程 2 曾经将 count 数据 11 更新到主存了,此时对于线程 1 来说,数据曾经过期了,等线程 1 执行完时,又将 11 同步到主存,后果导致两个线程都执行 count++,然而最终后果却小于正常值。

链接地址:https://blog.csdn.net/yj_android_develop/article/details/111094154

退出移动版