DecadeAnkle / JavaStudyRecord

Java学习内容汇总
0 stars 0 forks source link

并发编程-三大特性和volatile关键字 #4

Open DecadeAnkle opened 1 year ago

DecadeAnkle commented 1 year ago

并发编程的三大特性

原子性(Atomicity) 一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

Java中对基本数据类型的读取和赋值操作是原子性操作,例如

int i = 1;

下面的三个操作都不满足原子性 1.读取i的值,然后再赋值给j 2.读取i的值,加1,再写回主存 3.读取i的值,加1,再赋值给j

int j = i;
i++;
j = i + 1;

volatile是不能保证原子性的。

可见性(Visibility) 指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值

Java就是利用volatile来提供可见性的。 当一个变量被volatile修饰时,那么对它的修改会立刻刷新到主存,当其它线程需要读取该变量时,会去内存中读取新值。

普通变量则不能保证这一点,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。

通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性

当写一个volatile变量时,JVM会把该线程对应的本地内存中的共享变量刷新到主内存 当读一个volatile变量时,JVM会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量

有序性(Ordering 程序执行的顺序按照代码的先后顺序执行

指令重排序是指处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。例如,下面语句1,2的执行顺序先后不影响结果

int i = 0;              
boolean flag = false;
i = 1;                //语句1  
flag = true;          //语句2

但是下方的代码的执行顺序只能是1->2->3->4或者2->1->3->4,处理器在进行重排序时是会考虑指令之间的数据依赖性,所以不可能是4在3前

int a = 10;    //语句1
int r = 2;    //语句2
a = a + 3;    //语句3
r = a*a;     //语句4

在多线程环境中,语句1和语句2是没有依赖关系的,如果进行重排后,先执行语句2,导致语句1没有初始化,线程2执行 doSomething(context),就会导致程序出错,所以指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性

//线程1:
String context = init();   //语句1
flag = true;             //语句2

//线程2:
if(flag ){
  doSomething(context);
}

就算没有重排序,也可能发生所以,因为flag被赋值后,可能在读取之前还没有写回主存中,导致程序出错 所以需要用volatile来修饰flag

所以用volatile来修饰flag,禁止指令重排优化,保证有序性,再加上可见性,可以保证多线程的安全性

单例模式的应用

class Singleton{
    private volatile static Singleton instance = null;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if(instance==null) {
            synchronized (Singleton.class) {
                if(instance==null)
                    instance = new Singleton();
            }
        }
        return instance;
    }
}