Volatile关键字
通过该关键字保证不同线程对某个共享变量的可见性;
Volatile与synchronize区别
- Volatile只能修饰实例变量或者类变量,不能修饰方法、局部变量、方法参数、常量;
- synchronized关键字只能用于修饰方法或者代码块,不能修饰变量;
- Volatile修饰的变量可以为null,synchronized修饰的代码块的monitor不能为null;
- volatile不能保证原子性,synchronized是一种排他机制被其修饰的同步代码块不能被中途打断,因此是保证原子性的
- 两者都可保证有序性、可见性
- volatile不会使线程进入阻塞状态,后者会;
并发编程的三大特征
- 原子性:在一次或者多次的操作中,要么所有的操作都全部得到执行并且不会受任何因素的干扰而中断,要么所有操作都不执行;比如:转账1000,有两个操作转账账户扣除1000,收款账户增加1000,这两个操作必须原子操作;
基本数据类型的读取赋值操作都是原子性,引用类型变量的读取和赋值操作也是原子性的;
(1)、多个原子性操作合在一起就不再是原子操作了;
比如:
x=10;(原子性)
- 执行线程首先将x=10写入工作内存
- 然后再 将其写入主内存
x=y;(非原子性)
- 首先把y的的值从主内存中读取,然后存入当前线程的工作内存之中(如果已经在工作内存中,直接从工作内存中读取)(原子性)
- 在工作内存中将x的值修改为y的值,然后将x的写入主内存中;(原子性)
将第一二步何在一起就不是原子性了;
(2)、简单的读取赋值操作是原子性的,将一个变量赋值给另一个变量不是原子性的
(3)、java内存模型只保证了基本读取和赋值的原子性,其他不保证;
(4)、如果要想使代码片段具备原子性,可以使用synchronize关键字或者JUC中的lock;如果要时基本类型自夹具备原子性,可以使用 java.util.concurrent.atomic.*包下的类;
Volatile关键字不具备原子性;
- 可见性:当以一个线程修改了共享变量的值,另外的线程可以立即看到改变后的最新值;
保证可见性的方式:
- volatile修饰变量,对共享变量的写操作,在工作工作内存中完成后(其他线程必须重新从主存中获取最新值),会立即刷新到主内存中;
- synchronized关键字,保证同一时刻只有一个线程获得锁,然后执行同步方法,并且在释放锁之前,会将对变量的修修改刷新到主内存中;
- 通过JUC提供的显示锁,保证同一时刻只有一个线程获得锁,然后执行同步方法,并且在释放锁之前,会将对变量的修修改刷新到主内存中
Volatile关键字具备可见性;
- 有序性:代码在执行时的先后顺序,java在编译器和运行期做了优化,代码执行的先后顺序可能和我们编写代码的先后顺序不一致(这叫做指令重排序)来提高效率,但最终的结果是相同的;
需要注意:在单线程中,重排序始终能保证运算结果一致,但是在多线程中就不能保证了;
java提供了三种保证有序性的方式:
- volatile
- synchronized
- 显式锁
后两者采用同步的方式,同步代码在执新时与单线程一样,自然能保证有序性;
Volatile关键字具备有序性;
Volatile关键字的语义
被Volatile修饰的类变量和实例变量具备如下语义:
- 当一个线程修改了被volatile修饰的变量的值,其他线程会立即得到最新的值;
- 禁止对指令重排序