简述Volatile关键字

70 阅读2分钟

这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战

前言

Volatile是Java虚拟机提供的轻量级的同步机制,其在Java多线程占据了一部分重量,下面我们来简单的介绍一下他。

什么是Volatile?

Volatile 是一个由Java提供的关键字,其作用是提供的轻量级的同步机制

Volatile的特点

1 保证可见性

​ 在存在多个线程的情况下,一个线程修改了一个变量,其他线程可能使用的还是原来变量的值。这个就是未保证可见性锁导致的后果。

​ 保证可见性,会在一个线程修改了变量后去通知另一个线程重新去主存里更换新的变量的值

@SuppressWarnings({"all"})
public class JMMDemo {
    // 不加 volatile程序 会死循环
    // 可见性:对主线程的变量修改通知其他线程
    private volatile static int num = 0 ; // 可见性:对主线程的变量修改通知其他线程
    public static void main(String[] args) {
        new Thread(()->{
            while(num == 0){ // 线程1 对主内存的变化是不知道的

            }
        }).start();

        try{
            TimeUnit.SECONDS.sleep(1);
        }catch (Exception e){
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }
}

2 不保证原子性

​ 原子性:不可分割 线程A在执行任务的时候,不能被打扰,也不能被分割。要么同时成功,要么同时失败

​ 原子性在我们学习事务时是非常重要的特性,但是volatile不保证原子性

package com.cheng.Volatile;

// 不保证原子性
public class VDemo {
    // volatile 不保证原子性
    private volatile static int num = 0;
    public static void main(String[] args) {
        // 理论上来说应该是为20000
        for (int i = 0; i < 20; i++) {
            new Thread(() ->{
                for (int j = 0; j < 1000; j++) {
                   add();
                }
            }).start();
        }
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(num);
    }

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

为了保证使用Volatile关键字的情况下的原子性:在JUC包下给我们提供了原子类来供我们使用

image-20211113151044591

package com.cheng.Volatile;

import java.util.concurrent.atomic.AtomicInteger;

// 不保证原子性
public class VDemo {
    // volatile 不保证原子性,把int改成原子类来保证原子性
    private volatile static AtomicInteger num = new AtomicInteger();
    public static void main(String[] args) {
        // 理论上来说应该是为20000
        for (int i = 0; i < 20; i++) {
            new Thread(() ->{
                for (int j = 0; j < 1000; j++) {
                   add();
                }
            }).start();
        }
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(num);
    }

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

3 禁止指令重排

​ 指令重排:计算机并不一定会按照你写的程序代码顺序去实现

​ 代码执行步骤

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

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

我们期望执行顺序为 1234
但是执行的时候可能会变成2 1 3 4