JAVA小学生——volatile 是什么?

一、Volatile介绍

  1. volatile 是java虚拟机提供的一种轻量级的同步机制,特性如下:
    - 保证可见性
    - 不保证原子性
    会发生写覆盖的情况,一顿操作猛如虎,一看原值还是1
    - 禁止指令重排

  2. JMM Java内存模型

Java Memory Model 本身是一种抽象的概念,本身并不真实存在。它描述的是一组规范,通过这组规范定了程序种各个变量(包括实例字段、静态字段、和构成数组对象的元素)的访问方式。

JMM.png

JMM关于同步规定:

  1. 线程解锁前,必须把共享变量的值刷新回主内存
  2. 线程加锁前, 必须读取主内存的最新值到自己的工作内存
  3. 加锁解锁是同一把锁

JMM 三大特性:

  • 可见性: 只要当子线程将数据写回到主内存时,其他子线程都将及时接受到消息。
  • 原子性: 不可分割,完整性,也即某个线程正在操作某个具体业务时,中间不可以被加塞或者被分割,要不同时成功,要么同时失败。
  • 有序性
  1. 代码证明Volatile的可见性及JMM内存模型
public class MainApp {
    // 如果没有volatile修饰,线程B将一直死循环
    private volatile static int a = 0;

    public static void main(String... args) throws Exception {

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"\t come in");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            a = 10;
        }, "AA").start();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"\t come in");
            while (true) {
                if (a == 10) {
                    System.out.println("value  has been changed");
                    break;
                }
            }
        }, "BB").start();
    }

}

二、Volatile 不保证原子性解决

问题原因: 线程A在回写数据时,被其他线程加塞导致线程A挂起,当其他线程回写完毕,准备通知其他线程时,线程A直接执行了写的操作。

image.png

解决方案

  1. 加synchronized
  2. 使用Atomic

三、禁止指令重排

什么是指令重排?

image.png

案例1:


image.png

四、如何保证线程安全

image.png
package com.fullstack.atguigu;

public class SingletonDemo {

    private static volatile SingletonDemo instance = null;

    private SingletonDemo() {
        System.out.println(Thread.currentThread().getName() + "\tsingleton was created...");
    }

    /**
     * 方法1: 直接在method上添加synchronized,锁住整个方法,虽然解决了并发的问题,但是降低了性能
     */
    public static synchronized SingletonDemo getInstance() {
        if (instance == null) {
            instance = new SingletonDemo();
        }
        return instance;
    }

    /**
     * 方法2: 使用synchronized代码块锁住部分代码,注意要使用DCL(Double check Lock)
     * 同时防止指令重排,最好使用volatile修饰
     */
    public static SingletonDemo getInstance2() {
        if (instance == null) {
            synchronized (SingletonDemo.class) {
                if (instance == null) {
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

    /**
     * 在多线程模式下,如果没有加锁,会导致instance被初始化多次
     */
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                SingletonDemo.getInstance2();
            }, "T-" + i).start();
        }
    }
}


你可能感兴趣的:(JAVA小学生——volatile 是什么?)