千家信息网

Java中的volati关键字如何使用

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇"Java中的volati关键字如何使用"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看
千家信息网最后更新 2026年11月01日Java中的volati关键字如何使用

这篇"Java中的volati关键字如何使用"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇"Java中的volati关键字如何使用"文章吧。

一,什么是volatile关键字,作用是什么

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

作用是: 1.保证可见性 2.禁止指令重排 3.不保证原子性

二,什么是JMM

JMM(java 内存模型 Java Memory Model 简称JMM) 本身是一个抽象的概念,并不在内存中真实存在的,它描述的是一组规范或者规则,通过这组规范定义了程序中各个变量(实例字段,静态字段和构成数组对象的元素)的访问方式.

JMM的同步规定:

1.线程解锁之前,必须把共享变量刷新回主存

2.线程加锁锁之前,必须读取主存的最新值到自己的工作空间

3.加锁解锁必须是 同一把锁

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

三,可见性

可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

通过前面的 JMM介绍,我们知道各个线程对主内存的变量的操作都是各个线程各自拷贝到自己的工作内存中进行操作,然后在写回主内存中

这就可能存在一个线程a修改了共享变量X的值但还未写回主内存,又有一个线程b对共享变量X进行操作,但 此时线程a的工作内存的共享变量X对线程吧来说是不可见的,这种工作内存与主内存同步延迟的问题就造成了可见性问题

四,不保证原子性

原子性:某个线程在执行某项业务时,中间不可被加塞或分割,需要整体完整。要么同时成功,要么同时失败

    class MyData{    volatile int number = 0;    Object object = new Object();    public void addTo60(){        this.number = 60;    }        public void addPlusPlus(){        this.number++;    }        AtomicInteger atomicInteger = new AtomicInteger();        public void addAtomic(){        atomicInteger.getAndIncrement();    }}/** * 验证volatile的可见性 * 1.当number未被volatile修饰时,new Thread将number值改为60,但main线程并不知道,会一直在循环中出不来 * 2.当number使用volatile修饰,new Thread改变number值后,会通知main线程主内存的值已被修改,结束任务。体现了可见性 * * 验证volatile不保证原子性 * 1.原子性是指,某个线程在执行某项业务时,中间不可被加塞或分割,需要整体完整。要么同时成功,要么同时失败 * * 如何解决呢? * 1.使用synchronize * 2.使用AtomicInteger * */public class VolatileDemo {    public static void main(String[] args) {        //seeByVolatile();        atomic();    }    //验证原子性    public static void atomic() {        MyData myData = new MyData();        for (int i = 1; i <= 20; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    for (int j = 1; j <= 1000; j++) {                        /*synchronized (myData.object){                            myData.addPlusPlus();                        }*/                        myData.addPlusPlus();                        myData.addAtomic();                    }                }            }).start();        }        //等待上面20个线程全部计算结束        while (Thread.activeCount() > 2){            Thread.yield();        }                System.out.println(Thread.currentThread().getName() + "int finally number is " + myData.number);        System.out.println(Thread.currentThread().getName() + "AtomicInteger finally number is " + myData.atomicInteger);    }    //验证可见性的方法    public static void seeByVolatile() {        MyData myData = new MyData();        //第一个线程        new Thread(){            public void run(){                System.out.println(Thread.currentThread().getName() + " come in");                try {                    sleep(3000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                myData.addTo60();                System.out.println(Thread.currentThread().getName() + " update number to " + myData.number);            }        }.start();        //第二个线程 main        while (myData.number == 0){                }        System.out.println(Thread.currentThread().getName() + "mission is over");    }}

number++在多线程下是非线程安全,不是原子性操作?

五,禁止指令重排

计算机在执行程序时,为了提高性能,编译器和处理 器常常会对指令做重排,一般分为一下三种:

单线程的环境里指令重排确保最终执行的结果和代码顺序执行的结果一致

处理器在进行指令重排是必须 要考虑指令之间的数据依赖性

多线程的环境交替执行,由于编译器优化重排的存在,俩个线程使用变量能否保证一致性是无法确定的,无法预料的

线程操作资源类,线程1访问method1,线程2访问method2,正常情况顺序执行,a=6
多线程下假设出现了指令重排,语句2在语句1之前,当执行完flag=true后,另一个线程马上执行method2,a=5

所以volatile 禁止指令重排,从而避免多线程的 环境下出现执行乱序 的情况

六:使用volatile 的经典案例

单例DCL的代码

 单例DCL的代码public class SingletonDemo {    private static SingletonDemo instance = null;    private SingletonDemo(){        System.out.println(Thread.currentThread().getName() + "构造方法");    }        //DCL双端加锁机制    public static SingletonDemo getInstance(){        if (instance == null){            synchronized (SingletonDemo.class){                if (instance == null){                    instance = new SingletonDemo();                }            }        }        return instance;    }}

这种写法在多线程条件下可能正确率为99.999999%,但可能由于指令重排出错

原因在于某一个线程执行到第一次检测,读取到instance不为null,instance引用对象可能还没有完成初始化.

instance = new SingletonDemo();; 分为一下三步

  1. memory = allocate() //分配内存

  2. ctorInstanc(memory) //初始化对象

  3. instance = memory //设置instance指向刚分配的地址

2 ,3 步不存在数据依赖, 可以指令重排的执行顺序为 1 ,3 ,2,设置instance指向刚分配的地址,次数instance还没有初始化完

但此时instance不为null了,若正好此时有一个线程来访问,就出现了线程安全问题

所以需要添加volatile 关键字

public class SingletonDemo {    private static volatile SingletonDemo instance = null;    private SingletonDemo(){        System.out.println(Thread.currentThread().getName() + "构造方法");    }    //DCL双端加锁机制    public static SingletonDemo getInstance(){        if (instance == null){            synchronized (SingletonDemo.class){                if (instance == null){                    instance = new SingletonDemo();                }            }        }        return instance;    }}

以上就是关于"Java中的volati关键字如何使用"这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注行业资讯频道。

线程 内存 变量 指令 工作 原子 关键 关键字 内容 保证 同时 要么 验证 主存 代码 对象 拷贝 数据 方法 机制 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 博彦科技互联网金融 广州为想互联网科技有限公司 鸠鸠互联网科技 中资互联网科技 互联网与新科技 深圳齐家互联网科技 上海喔噻互联网科技 电子科技大学互联网 友谊互联网络科技 成都玖崟互联网科技有限公司乐商圈怎么样 宁夏众和广鑫互联网科技有限公司 杭州壁岐互联网科技有限公司 速维有品互联网信息科技(苏州)有限公司 广州市恰铎互联网科技有限公司 互联网金融科技公司的优势 伍佰万互联网科技怎么赚钱 皮皮虾互联网科技有限公司 云南互联网科技学费 马化腾互联网科技教学 科技互联网专业务虚会材料 上海卓越科技互联网 互联网科技宣传片 国外互联网科技网站 欧洲十大科技互联网公司 互联网保险科技创业 中国科技创新与世界互联网大会 如何创新互联网科技 互联网与数字科技关系 福建互联网展示科技馆设计 现代互联网科技公司
0