多线程笔记3——共享模型之内存

1. JAVA内存模型 JMM

JMM体现在在以下几方面:

原子性:指令不会受到线程上下文切换的影响

可见性:保证指令不会受CPU缓存的影响

有序性:指令不会受CPU指令并行优化的影响

1.1 可见性

多线程笔记3——共享模型之内存_第1张图片

因为t要频繁的从主存中读取run的值,JIT即时编译器会将run的值缓存到自己的高速缓存中,减少主存对run的访问,提高效率。

解决方法:

(1)volatile关键字:修饰成员变量和静态成员变量,可以强制从主存总读取。适用于一个线程写,剩下线程读。不保证原子性

(2)synchronized关键字:既能保证原子性也能保证可见性,但是成本高

1.2有序性

JVM会对指令进行指令重排来提高效率:在不改变结果的前提下,指令通过重排序和组合实现指令级并行

解决方法:volatile

例子:double-checked locking 懒汉单例模式

public final class Singleton{

    private static volatile Singleton INSTANCE = null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(INSTANCE == null){
            synchronized(Singleton.class){
                if(INSTANCE == null)
                    INSTANCE = new Singleton();
            }
        }
        return INSTANCE;
    }

}

如果不加volatile,可能会引起指令重排:线程1进入同步代码块刚刚执行完new Singleton(),对应的字节码为下面四条

指令21(调用构造方法new Singleton(),申请空间)和24(赋值,把构造好的那块地址赋值给INSTANCE)可能会重排。

1.3 volatile原理

底层实现原理是内存屏障, Memory Barrier

对volatile读指令前加入读屏障,对volatile写指令后会加入写屏障。读写屏障保证有序性和可见性

eg.

保证有序性:写屏障之前的代码不会被重排到写屏障后,读屏障之后的代码不会重排到屏障之前

保证可见性:num=2和 ready = true会先同步到主存中,而if(ready)之后的代码都会从主存中读取。 

多线程笔记3——共享模型之内存_第2张图片

2.CAS

compare and swap 无锁并发

原子整数:AtomicInteger, 

原子引用:AtomicReference(存在ABA问题),AtomicStampedReference, AtomicMarkableReference

原子数组:

原子累加器:LongAdder(效率更高,原理等效于MAPREDUCE)

LongAdder原理:缓存一致性,防止伪共享

3. Unsafe对象

由于是底层,不能直接获得,需要通过反射获得

你可能感兴趣的:(JAVA多线程)