Volatile的简单理解

问题:谈一谈你对volatile关键字的理解

volatile 是Java虚拟机提供的轻量级的同步机制

1. 保证可见性
2. 不保证原子性
3. 禁止指令重排
  • volatile引出了另一个问题 JMM
    • 概念: Java Memory Model (Java 内存模型 (不是JVM 内存模型)) ,JMM 是一种规范或者规则,抽象出来的,不是真实存在的,通过这种规范定义了程序中各个变量的访问机制
      在这里插入图片描述

    • 特点:由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为他们创建一个工作内存(栈空间),工作内存是每个线程的私有数据区域,而Java内存模型规定了所有的变量都存储在主内存中,主内存是共享的,所有线程都可以访问,但是线程对变量的操作必须在自己的工作内存中进行,首先要从主内存中拷贝一份副本到自己的工作内存中,然后对变量进行操作,操作完成后再写回主内存。注意:线程不能直接操作主内存的变量,变量副本拷贝,不同的线程无法访问对方的工作内存,线程之前的通信通过主内存来完成的

    JMM内存模型

    • 三个特性:

      1. 可见性
      2. 原子性
      3. 有序性
  • 理解了JMM ,之后再回到volatile这个关键字上

  1. volatile的可见性:

    • 验证volatile的可见性

      1. ​ 假如 int number =0;number变量之前根本没有添加volatile关键字修饰,没有可见性

      2. 添加volatile,可以解决可见性问题

        //资源类
        class MyData {
        
            int number = 0;
         
            public void addToNumber() {
                number = 60;
            }
        
        }
        
        
        public class VolatileDemo {
            
                public static void main(String[] args) {
                         MyData myData = new MyData();
                new Thread(
                        () -> {
                            System.out.println(Thread.currentThread().getName() + "\t come in");
                            try {
                                TimeUnit.SECONDS.sleep(3);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            myData.addToNumber();
        
                            System.out.println(Thread.currentThread().getName() + "\t updated:" + myData.number);
                        }
                        , "AAA").start();
        
                while (myData.number == 0) {
                   //如果Main感知到number值的更改程序就不会死循环
                }
                System.out.println(Thread.currentThread().getName() + "\t miss:" + myData.number);
                }
        }
        

    这段程序的结果是死循环 两个线程 一个是AAA线程 一个是Main 线程,解决办法是在 number变量前 加 volatile

2.验证volatile不保证原子性

  • 不可分割,完整性,即就是某个线程在做某个具体业务时,中间不可以被阻塞或者分割。要么成功,要么失败

在这里插入图片描述
在这里插入图片描述


 //写覆盖,写丢失
/*怎么解决:
 * 1. synchronized(太大了)
 * 2.用juc下的原子类
*/


class MyData {

    volatile int number = 0;
    AtomicInteger atomicInteger=new AtomicInteger();

    public void addToNumber() {
        number = 60;
    }

    /**
     * 请注意加了 volatile
     * <p>
     * number++; 3步
     */
    public void addPlusPlus() {
        number++;
    }

    public void addAtomic(){
        atomicInteger.getAndIncrement();
    }
        public static void main(String[] args) {
        MyData myData = new MyData();

        for (int i = 1; i <= 20; i++) {

            new Thread(() ->
            {
                for (int j = 1; j <= 1000; j++) {
                    myData.addPlusPlus();
                    myData.addAtomic();
                }
            }, String.valueOf(i)
            ).start();
        }

        //需要等待上面20个线程全部计算完成后,再用main 线程取得最终的结果值看是多少
        //main gc
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "\t int,finally number:" + myData.number);
        System.out.println(Thread.currentThread().getName() + "\t AtomicInteger ,finally number:" + myData.atomicInteger);
    }

}

3.volatile禁止指令重排

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

最后谈谈 volatile 的案例:

最经典的是单例模式:

public class SingletonDemo {
    private static volatile SingletonDemo instance = null;

    /**
     * DCL(Double Check Lock  双端检锁机制)
     */
    private SingletonDemo() {
        System.out.println(Thread.currentThread().getName() + "\t 构造方法");
    }

    public static SingletonDemo getInstance() {
        if (instance == null) {
            synchronized (SingletonDemo.class) {
                if (instance == null) {
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        //单线程
//        System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());
//        System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());
//        System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());
        //并发多线程

        for (int i = 1; i <= 1000; i++) {
            new Thread(() -> {
                SingletonDemo.getInstance();
            }, String.valueOf(i)).start();
        }

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值