Volatile

保证可见性

public class JMMDemo {
    //加了volatile,可以保证可见性
    private static volatile int num=0;
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            while(num==0){

            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        num=1;  //主内存中的值一旦发生变化如何通知另一个线程,另一个线程不知道主内存的值被修改过了 ==>Volatile
        System.out.println(num);
    }
}

不保证原子性

原子性:不可分割

线程A在执行任务时不能被打扰或分割

public class VDemo02 {
    //不保证原子性 => main 19209
    private static volatile int num=0;
    public static void main(String[] args) {

        //理论结果应为20000
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        //main线程 以及 GC线程
        while(Thread.activeCount()>2){
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName()+" "+num);
    }

    //增加synchronized关键字=> main 20000
    public static  void add(){
        num++;
    }
}

如果不增加synchronized和lock锁如何保证原子性?

num++根本不是一个原子性操作

反编译源码

  public static void add();
    Code:
       0: getstatic     #15                 //获得这个值
       3: iconst_1                          //获取num静态变量
       4: iadd                              //+1
       5: putstatic     #15                 //写回这个值
       8: return

使用原子类来解决原子性问题

Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力

public class VDemo02 {
    //不保证原子性 => main 19209
    private static volatile AtomicInteger num=new AtomicInteger(0);
    public static void main(String[] args) {

        //理论结果应为20000
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        //main线程 以及 GC线程
        while(Thread.activeCount()>2){
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName()+" "+num);
    }

    //增加synchronized关键字=> main 20000
    public static  void add(){
        num.getAndIncrement(); //加一方法:底层为CAS 效率极高 直接在内存中修改值,unsafe类类似于c语言中的指针
    }
}

禁止指令重排

你写的程序,计算机并不是按你写的顺序执行的

源代码 => 编译器优化的重排 => 指令并行也可能会重排 => 内存系统也会重排 => 执行


int x=1;  //1
int y=2;  //2
x=x+5;    //3
y=x*x;    //4

期望顺序:1 2 3 4

但是 2 1 3 4,1 3 2 4 也可以得出相同结果

不可能会是 4 1 2 3,处理器在进行指令重排时会考虑数据之间的依赖性

可能造成影响的结果 a b x y 默认都为0

线程A 线程B
x=a y=b
b=1 a=2

正常的结果 x=0 y=0,但是可能由于指令重排

线程A 线程B
b=1 a=2
x=a y=b

指令重排导致的诡异结果:x=2;y=1

只要加了Volatile可以避免指令重排

内存屏障,cpu指令,作用:

  1. 保证特定的操作的执行顺序
  2. 可以保证某些变量的内存可见性(利用这些特性可以保证volatile的可见性)

单例模式使用最多

posted @ 2022-01-18 17:46  一刹流云散  阅读(26)  评论(0编辑  收藏  举报