线程安全主要体现在 原子性 可见性 有序性
原子性指的是一个或多个操作在CPU中不会被中断的特性
原子性出现问题的原因在于 CPU切换时间片导致线程切换
使用 JDK 提供的Atomic开头的原子类、synchronized、LOCK。
Atomic类采用了CAS方法来保证原子性
1. java语言CAS底层如何实现?
利用unsafe提供的原子性操作方法。
2.什么是 ABA问题?怎么解决?(ABA问题是CAS机制的缺点)
当一个值从A变成B,又更新回A,普通CAS机制会误判通过检测。
利用版本号比较可以有效解决ABA问题。
1) CPU开销过大
在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很到的压力。
2) 不能保证代码块的原子性
CAS机制所保证的知识一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用synchronized了。
3) ABA问题
这是CAS机制最大的问题所在。
特别的是
在JAVA1.6以上版本,synchronized转变为重量级锁之前,也会采用CAS机制。
一个线程对共享变量的修改,另外一个线程能够立刻看到。
可见性问题产生原因
CPU缓存与内存导致的可见性问题。
如何解决可见性问题
使用volatile关键字保证可见性。
volatile关键字的目的是告诉虚拟机:
volatile修饰的变量,在每个读操作(load操作)之前都加上Load屏障,强制从主内存读取最新的数据。每次在assign赋值后面,加上Store屏障,强制将数据刷新到主内存。
每次访问变量时,总是获取主内存的最新值;
每次修改变量后,立刻回写到主内存。
volatile与synchronized的区别:
volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住q
volatile仅修饰变量;synchronized则可以修饰变量、方法、代码块
volatile仅保证可见性;synchronized则可以保证可见性和原子性
volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞
volatile修饰的变量会禁止指令重排序,因而程序不会被编译器优化;synchronized修饰的变量没有禁止指令重排序,因而程序可以被编译器优化
程序执行代码的顺序按照代码的顺序
Cpu对于指令优化排序,所带来的有序性问题
使用volatile关键字禁止指令重排;遵循happens-before原则。
内存屏障保证可见性
volatile修饰的变量,在每个读操作(load操作)之前都加上Load屏障,强制从主内存读取最新的数据。每次在assign赋值后面,加上Store屏障,强制将数据刷新到主内存。