Java内存模型(JMM)#
并发编程#
并发编程主要就是在处理两个问题,就是线程之间的通信和线程之间的同步。
通信是指线程之间应该如何交换信息
- 共享内存通信指线程A和B有共享的公共数据区,线程A写数据,线程B读数据,这样就完成了一次隐式通信。
- 消息传递通信是指线程之间没有公共数据,需要线程间显式的直接发送消息来进行通信
同步是指一种用来控制不同的线程之间操作发生相对顺序的机制。同步需要程序员显式的定义,主要是指定一个方法或者一段代码需要在线程之间互斥执行。
Java内存模型#
在 java 中,所有实例域、静态域和数组元素存储在堆内存中,堆内存在线程之间共享,共享变量才会有内存可见性问题,所以会受到内存模型的影响。
JMM 决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM 定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读 / 写共享变量的副本。
重排序#
在程序执行时,为了提高性能,编译器和处理器常常会对指令做重排序。主要有三种:编译器优化的重排序,指令级并行重排序,内存系统重排序。
上述的 1 属于编译器重排序,2 和 3 属于处理器重排序。这些重排序都可能会导致多线程程序出现内存可见性问题。对于编译器,JMM 的编译器重排序规则会禁止特定类型的编译器重排序(不是所有的编译器重排序都要禁止)。对于处理器重排序,JMM 的处理器重排序规则会要求 java 编译器在生成指令序列时,插入特定类型的内存屏障指令,通过内存屏障指令来禁止特定类型的处理器重排序(不是所有的处理器重排序都要禁止)。
happen-before规则#
happens-before 规则就是一套约定,用来告诉 JVM:哪些重排序是不允许的,哪些内存更新必须立刻对其他线程可见。
单一线程顺序规则:在一个线程内,在程序前面的操作happen-before于后面的操作。
监视器锁规则:对一个锁的解锁操作happen-before于后面对同一个锁的加锁操作。
volatile 变量规则:对一个 volatile 变量的写操作happen-before于后面对这个变量的读操作。
传递性规则:如果A happen-beforeB,B happen-before C , 则 A happen-before C
线程start()规则:如果线程A执行操作ThreadB.start(),那么A线程的ThreadB.start()操作happen-before于线程B中的任意操作。
join规则:如果线程A执行操作ThreadB.join()并成功返回,那么B线程内的任意操作happen-before于线程A从ThreadB.join()操作成功返回。
Synchronized#
对于普通同步方法,锁是当前实例对象
对于同步方法块,锁是Synchronized括号里面指定的对象(指定的对象也可以用this,即指定当前实例对象)
对于静态同步方法,锁是当前类的Class对象
