Concurrency | Concurrent on JMM
by Botao Xiao
Java Memory Model and Thread
TPS(Transaction per second)是衡量服务性能好坏的一个重要指标。代表着一秒内服务端平均能响应的请求总数。
硬件效率一致性
Java内存模型(Java Memory Model)
- JMM定义了程序中每个变量的访问规则。此处变量指的是实例字段,静态字段和构成数组对象的元素,这些变量都不是线程私有的。
- Main Memory 主内存
- 这是虚拟机内存的一部分。
- 所有的变量都存储在主内存中。
- Working Memory 工作内存
- 保存所有当前线程需要用到的变量在主内存中的副本拷贝。
- 线程对变量的所有操作都必须在工作内存中完成,不能直接读写主内存的变量。
- Main Memory 主内存
内存间的交互操作
- lock:作用于主内存的变量,它把一个变量标识为一条线成独占的状态。
- unlock:作用于主内存的变量,把一个出于锁定状态的变量释放出来,释放后的变量才能被别的线程锁定。
- read:作用于主内存的变量,将主内存中的变量传输到线程的工作内存中,以便随后load使用。
- load:作用于工作内存的变量,将read从主内存中得到的变量放入工作内存的变量副本中。
- use:作用于工作内存的变量,它把工作内存中的一个变量的值传递给执行操作,每当虚拟机需要用到变量的值的时候会执行这个操作。
- assign:作用于工作内存的变量。当JVM需要对某个变量进行赋值时,将调用该指令对工作内存中的变量进行赋值。
- store: 将工作内存的一个变量的值传到主内存,为了write做准备。
- write:作用于主内存的变量,将store得到的变量的值写入。
volatile
- volatile不是线程安全的,能保证的是在进行读取的时候这个值是主内存中的最新值。但是同时别的线程可能也已经获取了值,在写回主内存中时候可能造成线程不安全。
public class VolatileTest{ public static volatile int count = 0; public static void increase(){ count ++; } public static void main(String[] args) throws InterruptedException { Thread[] threads = new Thread[20]; for(int i = 0; i < 20; i++){ threads[i] = new Thread(() -> { for(int j = 0; j < 10000; j++) increase(); //每个线程都自增一个volatile变量。 }, "thread-" + i); threads[i].start(); } for(Thread t : threads) t.join(); System.out.println(count); //最后得到结果一般都小于200000 } }
- 写入时要保证修改变量后要立刻写入主内存中,保证其他变量都能看到线程对V的改变。
- 保证volatile修饰的变量不会被指令重排序优化。
原子性与可见性与有序性
- 原子性
- 虚拟机没有把lock和unlock开放给用户使用,却提供了monitorenter和monitorexit来隐式的使用lock和unlock。反映到Java的上层代码中就是syncronized,所以在synchronized之间的代码是有原子性的。
- 可见性
- 通过volatile变量可以实现。与普通变量相比,volatile变量可以保证当前变量是主内存中的最新值。
- syncronized和final也可以保证可见性。
- 有序性
- 如果在线程内部观察,所有的线程都是有序的,如果在一个线程观察另一个线程,所有的操作都是无序的。
先行发生原则(happens-before)
- 程序次序原则(Program Order Rule):在一个线程内部,按照代码的顺序,书写在前面的操作先行发生于书写在后面的操作。
- 管程锁定规则(Monitor Lock Rule):同一把锁,上一次unlock之前的代码在此次的lock之前。
- volatile变量规则(Volatile Variable Rule):对于一个volatile变量,写操作优先于对这个变量的读操作。
- 线程启动规则(Thread Start Rule):Thread对象的start方法优先于此线程的每一个操作。
- 线程终止规则(Thread Termination Rule):线程所有的指令优先于线程的终止。
- 线程中断规则(Thread Interrupt Rule):对线程interrupt方法的调用先行发生于被中断线程的代码检测到中段发生的事件。
- 对象终结规则(Finalizer Rule):对象的初始化优先于对象对于Finalize方法的调用。
- 传递性(Transitivity):A优先于B,B优先于C,所以A优先于C。
Subscribe via RSS