Volatile

suaxi
2021-02-19 / 0 评论 / 45 阅读 / 正在检测是否收录...

Volatile

1、保证可见性
package com.sw.volatileDemo;

import java.util.concurrent.TimeUnit;

/**
 * @Author suaxi
 * @Date 2021/2/19 22:07
 */
public class JMMTest {
    //不加volatile程序会死循环
    //加上volatile保证了可见性
    private volatile static int num = 0;
    public static void main(String[] args) { //main线程

        new Thread(() ->{ //线程1
            while (num == 0){

            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        num = 1;
        System.out.println(num); //输出num=1,main线程将num改为1后,线程1还在循环while(num == 0)
    }
}


2、不保证原子性

原子性:任务在执行的过程中不能被分割,也不能被干扰,一组操作要么都成功,要么都失败

不保证原子性测试实例:

package com.sw.volatileDemo;

/**
 * @Author suaxi
 * @Date 2021/2/19 22:25
 * volatile不保证原子性
 */
public class VolatileTest01 {
    private volatile static int num = 0;

    public static void add() {
        num++;
    }

    public static void main(String[] args) {

        for (int i = 0; i < 20; i++) {
            new Thread(() ->{
                for (int j = 0; j < 1000; j++) {
                    //理论上默认相加的结果等于2000
                    add();
                }
            }).start();
        }

        while (Thread.activeCount() > 2){ //Java中main线程和gc线程默认运行
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName()+" "+num);
        //19359
        //18089
        //不使用锁的实际相加的情况不等于2000
        //volatile不保证原子性
    }
}


如何在不使用synchronized和lock锁的情况下保证原子性?

1.不保证原子性测试用例源码分析.png

图片来源:狂神说Java

答:使用原子类解决

package com.sw.volatileDemo;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author suaxi
 * @Date 2021/2/19 22:25
 * volatile不保证原子性
 * 使用Atomic原子类解决原子性问题
 */
public class VolatileTest02 {
    private volatile static AtomicInteger num = new AtomicInteger();

    public static void add() {
        //num++;
        num.getAndIncrement(); //AtomicInteger中的 +1 方法
    }

    public static void main(String[] args) {

        for (int i = 0; i < 20; i++) {
            new Thread(() ->{
                for (int j = 0; j < 1000; j++) {
                    //理论上默认相加的结果等于2000
                    add();
                }
            }).start();
        }

        while (Thread.activeCount() > 2){ //Java中main线程和gc线程默认运行
            Thread.yield();
        }

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


3、禁止指令重排

指令重排:源代码 ---> 编译器优化的重排 ---> 指令并行也可能会重排 ---> 执行


假设x y a b四个数的默认值为0

线程1线程2
x = ay = b
b = 1a = 2

正常的执行结果为:x=0,y=0

线程1线程2
b = 1a = 2
x = ay = b

指令重排可能导致的结果:x =2,y=1


Volatile可以避免指令重排:

内存屏障,作用:

1、保证操作的执行顺序

2、保证某些变量的内存可见性

2.内存屏障.png

0

评论 (0)

取消