目录
4、Volatile关键字和synchronized关键字的比较
一、有序性、可见性、原子性
1、有序性
多线程指令重排序:
指令重排分为硬件层面和虚拟机层面,此处只讲虚拟机层面
目的:解决内存操作速度慢于CPU操作速度所带来的CPU空置问题,尽可能充分利用CPU
表现形式:虚拟机会按照特定的规则将程序打乱,使得写在后面的程序可能会先执行,写在前边的程序可能会后执行。
2、可见性
所谓的可见性,是线程A可以读取到线程B修改后的变量的值,改变线程从私有工作内存取变量值的方式,强制线程从主内存取值。
3、原子性
原子是世界上最小的单位,具有不可分割的特性
二、volatile关键字的作用——可见性
关键字volatile的主要作用是使变量在多个线程中可见。通过使用volatile关键字,强制从公共内存中读取变量的值,不允许线程内部对变量进行缓存和重排序、不加锁、不阻塞,实现更轻量级的线程同步。
内存结构图:
1、Volatile关键字使用条件
(1)对变量的写操作不依赖于当前值
(2)该变量没有包含在具有其他变量的不变式中
2、Volatile关键字使用场景
(1)用于指示发生了一个重要的一次性事件,例如完成初始化或请求停机
(2)用作状态标志
状态标记的公有特性,通常只有一种状态转换:比如标志从false转换为true,然后停止
3、Volatile关键字使用缺点
只有在状态真正独立于程序内其他内容时才能使用Volatile,但大多数编程环境都不满足上述条件,因此使用volatile比使用锁更加容易出错(谨慎)
4、Volatile关键字和synchronized关键字的比较
1、多线程访问volatile不会发生阻塞,而synchronized会出现阻塞
2、volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性和可见性。
注:线程安全包括原子性和可见性两个方面,java的同步机制都是围绕这两个方面来确保线程安全的。
5、volatile的非原子性
关键字volatile虽然实现变量在线程之间的可见性,但是不能保障同步性(原子性),所以执行的结果也是存在问题的。
测试代码如下:
public class MyThread extends Thread {
public volatile static int count;
private static void addCount() {
for (int i = 0; i < 100; i++) {
count++;
}
System.out.println("打印相加结果:" + count);
}
@Override
public void run() {
addCount();
}
public static void main(String[] args) {
// 定义100个线程
MyThread[] myThreads =