IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 2.JAVA大厂面试第二季-volatile -> 正文阅读

[Java知识库]2.JAVA大厂面试第二季-volatile

谈谈对volatile的理解

volatile是java虚拟机提供的轻量级同步机制,它保证了可见性,不保证原子性,并且禁止指令重排

--JMM内存模型之可见性

JMM本身是一种抽象的概念并不真实存在,它描述的是一组规则或者规范,通过这一组规范定义了程序中的各个变量的访问方式。JMM要求保证可见性,原子性和有序性

JMM关于同步的规定:

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

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

所谓可见性就是:当一个线程在本地内存更改了数据并且把变量写回主内存的适合要通知其他线程,现在这个变量已经更改了。

验证可见性的demo


/**
 * @ Author wuyimin
 * @ Date 2021/9/6-20:47
 * @ Description 验证volatile的可见性
 */
public class JMMTest {
    public static void main(String[] args) {
        //MyData myData = new MyData();
        MyDataWithVolatile myData=new MyDataWithVolatile();//添加了可见性任务之后才会打印任务完成
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"进来了");
            try{
                TimeUnit.SECONDS.sleep(3);
                myData.change();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"更新了值");
        },"A").start();
        //第二个线程直接使用我们的主线程
        while (myData.num==0){}//如果我们不设置可见性那么就会一直在这里自旋导致下面那句话不会打印
        System.out.println(Thread.currentThread().getName()+"任务完成");
    }
}
//不添加volatile关键字
class MyData{
    int num=0;
    public void change(){
        this.num=60;
    }
}
//不添加volatile关键字
class MyDataWithVolatile{
    volatile int num=0;
    public void change(){
        this.num=60;
    }
}

两种运行结果

?

?volatile不保证原子性

    /*
    不保证原子性
     */
    private static void notProvideAtomicTest() {
        MyDataWithVolatile myData = new MyDataWithVolatile();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.add();//理论上来说保证原子性20个线程加完以后应该就是20000
                }
            }, "" + i).start();
        }
        //等待上面的线程计算完成,再使用main线程查看最后结果(这一步不影响定论,但是少了这一步最终结果会大量减少 13452
        while (Thread.activeCount() > 2) {
            Thread.yield();//线程让步,可以让低于当前优先级的线程优先,
        }
        System.out.println(myData.num);//19107..每次的值都不一样
    }

//添加volatile关键字
class MyDataWithVolatile{
    volatile int num=0;
    public void change(){
        this.num=60;
    }
    public void add(){
        num++;//num++其实有三个操作getfield拿到原始值,iadd执行加一操作,执行putfield把累加后的值写回
    }
}

?如何不加synchronized来解决?(synchronized过于重量级)

使用包装类AtomicInteger

//不添加volatile关键字
class MyDataWithVolatile {
    volatile AtomicInteger atomicNum=new AtomicInteger(0);
    public void atomicAdd() {
        atomicNum.getAndIncrement();
    }
}

指令重排:

计算机再执行程序的适合,为了提高性能,编译器和处理器常常会对指令做重排

正常执行时1234,底层重写会考虑数据依赖性可能是2134,1324但是怎么也不会把语句4放到前面。下面给出一个指令重排导致的结果不一致的例子:

单例模式在多线程情况下产生的不安全问题--懒汉式问题改成双重检查锁模式

public class Singleton3 {
    private volatile static Singleton3 instance;//防止指令重排,保证可见性,不保证原子性
    private Singleton3(){}
    public static Singleton3 getInstance(){
        if(instance==null){
            synchronized (Singleton3.class){
                if(instance==null){
                    instance=new Singleton3();
                }
            }
        }
        return instance;
    }
}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-08 10:32:21  更:2021-09-08 10:32:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 7:33:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码