首页 > 编程语言 >Java volatile

Java volatile

时间:2023-01-06 12:22:25浏览次数:37  
标签:singleton Java 变量 线程 内存 volatile public

JMM:Java内存模型

  要想学习volatile,就不得不了解JMM。JVM运行程序的实体是线程,每个线程在被创建时JVM都会为其创建一个自己私有的工作内存。而Java内存模型规定所有的变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但是线程对这些变量的操作只能在自己的工作内存中进行,不能直接操作主内存中的变量,要先将变量从主内存中拷贝到线程自己的工作内存中再对其进行操作,操作完成后再将操作后的变量写回主内存当中,因此不同的线程也无法访问对方的工作内存,线程间的通信必须通过主内存来完成。

关于JMM需要知道的点:

  • 工作内存和Java内存模型并不是真实存在于java虚拟机中,而是一种规范和定义。

  • 共享变量存储于主内存之中,每个线程都可以访问。这里的变量指的是实例变量和类变量。局部变量是线程私有的,不存在共享。

  • 每个线程都有私有的工作内存或者称为本地内存,工作内存存储的是共享变量的副本。

  • 线程不能直接操作主内存,只有先操作了工作内存之后才能写入主内存。

  • 不同的线程不能直接访问对方工作内存中的变量,线程间变量的值传递需借助主内存作为中转来完成。

volatile

  volatile是java虚拟机提供的轻量级同步机制。其三大特性为保证可见性、不保证原子性、保证有序性(禁止指令重排)。

volatile保证可见性

  如果A线程和B线程同时获取主内存中的同一个变量,之后A线程修改了这个变量,但是此时B线程并不知道A线程已经对数据进行了修改,所以要具有可见性让线程之间进行通讯。当线程A修改完以后线程B也能知道此时该变量的值已经变为A修改后的数据,实现可见性

public class VolatileTest {

    public static void main(String[] args) throws InterruptedException {
        Demo demo = new Demo();
        new Thread(() -> {
            System.out.println(Thread.currentThread());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            demo.add();
        }).start();
        while (demo.i == 0) {
            //
        }

    }
}

class Demo {
    int i = 0;
    public void add() {
        i ++;
    }
}

volatile不保证原子性

volidate保证有序性(即:禁止指令重排序)

volatile实现禁止指令重排优化,从而避免多线程环境下程序出现乱序执行的现象。

如果不使用volatile,在多线程环境中线程交替执行,由于编译器优化重排,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。

计算机在执行程序时,为了提高性能,编译器和处理器通常会对指令做出重排。为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

总结

适用场景

  1. 某个属性被多个线程共享,其中一个线程修改了此属性,其他线程可以立即获得修改后的值,比如线程循环标识boolean flag;
  2. volatile还可以用于单例模式,可以解决单例双重检查对象初始化代码执行乱序问题。
public class Singleton {
  
    private volatile static Singleton singleton = null;
  
    public Singleton() {
        System.out.println(Thread.currentThread().getName() + "生成singleton");
    }
  
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

上述单例模式中使用了双重检验,如果不用volatile关键字修饰属性又会怎样?
  单线程情况下,属性singleton不加volatile关键字也不会出现任何问题;但是多线程情况下,会出现指令重排序的情况,就有可能出现空指针问题。首先需要了解的是对象创建包含下面三个过程:1.分配内存空间;2.调用构造函数,初始化对象;3.返回地址给引用。由于步骤2和步骤3不存在数据依赖关系,而且无论是重排前还是重排后的执行结果在单线程中并没有发生改变,因此这种重排优化是允许的。若此时先执行步骤3,步骤2还未执行完,另一个线程来执行if (singleton == null)会返回false,此时对象未完全生成,是个半成品,当访问对象方法或属性时,就会抛出空指针异常。使用volatile避免指令重排序,同时保证写回主存中的对象只有一个,实现真正意义上的单例。

标签:singleton,Java,变量,线程,内存,volatile,public
From: https://www.cnblogs.com/antidogmatist/p/17030082.html

相关文章

  • Java并发容器之LinkedBlockingQueue源码分析
    一、简介LinkedBlockingQueue是java并发包下一个以单链表实现的阻塞队列,它是线程安全的,至于它是不是有界的,请看下面的分析。二、源码分析2.1属性 //容量private......
  • Java的四种引用方式
    java内存管理分为内存分配和内存回收,都不需要程序员负责,垃圾回收的机制主要是看对象是否有引用指向该对象。java对象的引用包括  强引用,软引用,弱引用,虚引用Java中提供......
  • 2023.1.06 java打印杨辉三角(二维数组)
    publicclassyanghui{publicstaticvoidmain(String[]args){int[][]yanghui=newint[10][];for(inti=0;i<yanghui.length;i++){......
  • [JavaScript]分解url参数
    分解页面url传入参数转载:来自https://www.jianshu.com/p/6dd040f6800dfunctioninit_params(){varurl=location.search;//获取url中"?"符后的字串......
  • 『中级篇』docker之CI/CD持续集成—真实JAVA-Maven项目的CI演示(73)
    ICD。项目通过gitlab和gitlabCI进行CICD。源码地址:​​​https://github.com/limingios/gitlabci-maven​​​源码:​​https://github.com/limingios/docker/tree/master/......
  • Java 追加写入文件内容
    FileWriter类中有一构造方法是:publicFileWriter(Filefile,booleanappend),append参数如为true则追加写入;false为覆盖写入;Stringstr="abc";FileWriterf......
  • 【LeetCode2180】[Go/C++/C#/Ruby/Swift/Kotlin/Rust/PHP/TS/Racket/Dart/Java/Elixir
    [toc]题解地址https://leetcode.cn/problems/count-integers-with-even-digit-sum/solutions/2047123/by-yhm138_-w8co/lc2180代码//点击指定元素document.querySel......
  • Java调用CMD命令
    ProcessBuilderbuilder= newProcessBuilder(commands);1、在使用ProcessBuilder时,如果commands使用{"java-version"}这种带空格的命令,可能会报文件找不到的错误,最......
  • [Java 8] (1) 函数式编程简介
    思维方式的转变以从一个城市集合中寻找是否存在Chicago为例:习惯的方式booleanfound=false;for(Stringcity:cities){if(city.equals("Chicago")){......
  • JAVA常用的工具类
    1集合工具类1.1java.util.Collections使用的基本list示意List<Integer>list=newArrayList<>();list.add(2);list.add(1);list.add(3);1.1.1基本操作Collections.so......