Java并发三大特性

  • 可见性
  • 问题
  • 解决办法
  • CPU缓存体系
  • 缓存行
  • 硬件保证缓存一致性
  • 有序性
  • 问题
  • 原子性
  • 问题
  • 解决
  • 悲观锁
  • 乐观锁
  • 使用场景


可见性

问题

每个线程读取r会在内存中拷贝到自己线程使用的缓存中,当其中一个线程改变r值,另外一个线程是感知不到的。

java并发和并行的区别 java并发性_面试

解决办法

volatile保证可见性

CPU缓存体系

java并发和并行的区别 java并发性_缓存_02

缓存行

cacheLine:因为程序局部性原理,按快读取,可以提高效率。一般64字节。

伪对齐:线程读取到自己不需要数据,而且要为不需要的数据同步花费时间,执行效率底下。
解决:@Contended缓存行对齐,避免伪对齐。

java并发和并行的区别 java并发性_面试_03

硬件保证缓存一致性

缓存一致性协议,保证线程之间缓存内容一致。
Intel: MESI一致性协议,cpu每个cache line标识4中状态(修改,共享,独占,无效)。

有序性

问题

大家都知道cpu执行没有前后依赖的指令是乱序执行的,当然java在执行字节码指令也是乱序的,只要保证单线程情况下输出结果是一致就可以。这样可以极大的提升执行效率。

但是也有一些问题:在多线程情况会出问题的。下面举一个对象半初始化的例子。

Object o=new Object();

字节码指令:

java并发和并行的区别 java并发性_面试_04


有肯能在执行new开辟内存空间后,就先去执行astore指令,告诉别的线程对象已经初始化完毕,但是执行初始化方法的指令还有调用,导致对象的数据错误。

原子性

问题

多线程条件下访问共享数据时候,产生竞争,导致数据不一致;需要线程同步保证数据一致。

n++(不是原操作): 1.读取数据 、2.加1 、3.赋值写会内存

解决

悲观锁

synchronized保证原子性: 本质(并发编程序列化)

monitor(管程):锁
临界区:锁的代码段,也就是锁的粒度。

synchronized可以保证可见性,加锁解锁会和内存进行同步。

乐观锁

CAS自旋锁

AtomicInteger使用CAS保证原子性。

java并发和并行的区别 java并发性_可见性_05

Hostpot源码:

java并发和并行的区别 java并发性_可见性_06

使用指令lock cmpcxg指令,但是这条指令也不是原子性的,使用lock锁实现的。

java并发和并行的区别 java并发性_java_07

使用场景

synchronized:临界区比较大,等待人多。
CAS自旋锁:等待人少。