Contents
  1. 1. 内存模型
    1. 1.1. 不一致性问题解决方式
  2. 2. 并发
    1. 2.1. 可见性
    2. 2.2. 有序性
    3. 2.3. 原子性
      1. 2.3.1. 参考文档

内存模型

如图所示,在多线程运行时,在多核 CPU 中,每条线程可能运行在不同的 CPU 中,即每个线程运行时有自己的高速缓存。而共享变量存于主存,当程序运行时,会从主存读取共享变量的值,然后复制一份到高速缓存当中,然后 CPU 执行指令对共享变量进行操作,然后将数据写入高速缓存,最后将高速缓存中最新的变量值刷新到主存当中。

如此一来,当多个线程在多核 CPU 中并发执行时,会存在竞态条件(当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。 导致竞态条件发生的代码区称作临界区),也就是说,如果一个变量在多个 CPU 中存在缓存,将导致线程不安全。

为保证程序的线程安全,实际上就是需要对多线程的同步,而多线程的同步本质上就是多线程通信的问题。操作系统里面定义了几种进程通信的方式:

  1. 管道 pipeline
  2. 信号 signal
  3. 消息队列 messsage queue
  4. 共享内存 shared memory
  5. 信号量 semaphore
  6. 套接字 Socket

不一致性问题解决方式

  为了解决缓存不一致性问题,通常来说有以下2种解决方法:

  1)通过在总线加LOCK#锁的方式

  2)通过缓存一致性协议

这两者都是硬件层面上提供的方式。

并发

Java里面进行多线程通信的主要方式就是共享内存的方式,共享内存主要的关注点有两个:可见性和有序性。加上复合操作的原子性,我们可以认为Java的线程安全性问题主要关注点有3个:

可见性

可见性指的是一个线程对变量的写操作对其他线程后续的读操作可见。由于现代CPU都有多级缓存,CPU的操作都是基于高速缓存的,而线程通信是基于内存的,这中间有一个 Gap, 可见性的关键还是在对变量的写操作之后能够在某个时间点显示地写回到主内存,这样其他线程就能从主内存中看到最新的写的值。volatile 和 synchronized 及显式锁,原子变量这些同步手段都可以保证可见性。可见性底层的实现是通过加内存屏障(埋坑)实现的:

  • 写变量后加写屏障,保证 CPU 写缓冲区的值强制刷新回主内存
  • 读变量之前加读屏障,使缓存失效,从而强制从主内存读取变量最新值

有序性

有序性指的是数据不相关的变量在并发的情况下,实际执行的结果和单线程的执行结果是一样的,不会因为重排序的问题导致结果不可预知。volatile、final、synchronized,显式锁都可以保证有序性。

有序性的语意有几层,

  1. 最常见的就是保证多线程执行的串行顺序

  2. 防止重排序引起的问题

  3. 程序执行的先后顺序,比如JMM定义的一些Happens-before规则

重排序的问题是一个单独的主题,常见的重排序有3个层面:

  1. 编译级别的重排序,比如编译器的优化

  2. 指令级重排序,比如CPU指令执行的重排序

  3. 内存系统的重排序,比如缓存和读写缓冲区导致的重排序

原子性

Java内存模型JMM解决了可见性和有序性的问题,而锁解决了原子性的问题。

原子性是指某个(些)操作在语意上是原子的。比如读操作,写操作,CAS(compare and set)操作在机器指令级别是原子的,又比如一些复合操作在语义上也是原子的,如先检查后操作if(xxx == null){}

有个专有名词竞态条件来描述原子性的问题。

竞态条件(racing condition)是指某个操作由于不同的执行时序而出现不同的结果,比如先检查后操作。

volatile变量只保证了可见性,不保证原子性, 比如a++这种操作在编译后实际是多条语句,比如先读a的值,再加1操作,再写操作,执行了3个原子操作,如果并发情况下,另外一个线程很有可能读到了中间状态,从而导致程序语意上的不正确。所以a++实际是一个复合操作。

加锁可以保证复合语句的原子性,sychronized可以保证多条语句在synchronized块中语意上是原子的。显式锁保证临界区的原子性。原子变量也封装了对变量的原子操作。非阻塞容器也提供了原子操作的接口,比如putIfAbsent。

理解可见性,有序性,原子性是理解并发编程的一个重要基础。

参考文档

  1. http://www.cnblogs.com/dolphin0520/p/3920373.html#!comments
  2. http://www.infoq.com/cn/articles/java-memory-model-1
  3. https://ress.infoq.com/minibooks/java_memory_model/zh/pdf/think_deep_in_java_mem_model.pdf
Contents
  1. 1. 内存模型
    1. 1.1. 不一致性问题解决方式
  2. 2. 并发
    1. 2.1. 可见性
    2. 2.2. 有序性
    3. 2.3. 原子性
      1. 2.3.1. 参考文档