美文网首页
JAVA小学生——volatile 是什么?

JAVA小学生——volatile 是什么?

作者: 我不想成为胖子 | 来源:发表于2019-04-24 23:38 被阅读0次

    一、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 是什么?

          本文链接:https://www.haomeiwen.com/subject/yyibgqtx.html