Open DecadeAnkle opened 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 程序执行的顺序按照代码的先后顺序执行
当写一个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; } }
并发编程的三大特性
Java中对基本数据类型的读取和赋值操作是原子性操作,例如
下面的三个操作都不满足原子性 1.读取i的值,然后再赋值给j 2.读取i的值,加1,再写回主存 3.读取i的值,加1,再赋值给j
volatile是不能保证原子性的。
Java就是利用volatile来提供可见性的。 当一个变量被volatile修饰时,那么对它的修改会立刻刷新到主存,当其它线程需要读取该变量时,会去内存中读取新值。
普通变量则不能保证这一点,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性
指令重排序是指处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。例如,下面语句1,2的执行顺序先后不影响结果
但是下方的代码的执行顺序只能是1->2->3->4或者2->1->3->4,处理器在进行重排序时是会考虑指令之间的数据依赖性,所以不可能是4在3前
在多线程环境中,语句1和语句2是没有依赖关系的,如果进行重排后,先执行语句2,导致语句1没有初始化,线程2执行 doSomething(context),就会导致程序出错,所以指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性
就算没有重排序,也可能发生所以,因为flag被赋值后,可能在读取之前还没有写回主存中,导致程序出错 所以需要用volatile来修饰flag
所以用volatile来修饰flag,禁止指令重排优化,保证有序性,再加上可见性,可以保证多线程的安全性
单例模式的应用