首页 > 编程语言 >【技术积累】Java里的volatile关键字到底能干嘛?

【技术积累】Java里的volatile关键字到底能干嘛?

时间:2023-08-17 18:56:32浏览次数:29  
标签:Java show 关键字 volatile 内存 7.4 线程 public

7.4 最害怕的一集 - volatile

7.4.1 最简单的一集 - volatile 语义 (难度 : ⭐)

读 -> 读一个 volatile 必须从 主内存读

写 -> 写一个 volatile 会把 本地内存 写到 主内存去

 

7.4.2 最好理解的一集 - volatile 保证了 可见性 ( 难度 : ⭐ )

public class VolatileSTest {

   public static void main(String[] args) throws InterruptedException {
       Data data = new Data();
       new Thread(() -> {
           while (true) {
               if (data.bool) {
                   System.out.println("溜了溜了,线程结束了喵!");
                   break;
              }
          }
      }).start();

       TimeUnit.SECONDS.sleep(1);
       // 保证新线程后修改
       new Thread(() -> {
           data.bool = true;
      }).start();
  }
}
class Data {
  // boolean bool = false;
   // volatile boolean bool = false;
}

理解方法:

  • 如果去掉 volatile : 就会发现 死循环了

  • 如果不去掉 volatile : 就会发现 没死循环

 

自然就很好理解他的 可见性保证

7.4.3 最难复现的一集 - volatile 的 有序性保证 ( 难度 : ⭐⭐⭐⭐ )

为什么给四颗⭐, 因为首先,凭空想想不到,而且真要复现出重排序很难, 个人运行了几千次线程都出不来

class Number {
   int i = 1;
   volatile int v = 1;

   public void set() {
       i = 2;
       v = 2;
  }

   public void show() {
       if (v == 2) {
           System.out.println("t = " + i);
      }
  }
}

重排序

问题一

两个线程 :

一个调用 show , 一个线程调用 set

重复一遍 !!!! 两个线程: 一个调用 show , 一个线程调用 set

重复一遍 !!!! 两个线程: 一个调用 show , 一个线程调用 set

重复一遍 !!!! 两个线程: 一个调用 show , 一个线程调用 set

因为在单线程中 , 先调用set方法 ,再调用show方法, 因为 happens-before , 是不会允许重排序的

线程一: {
      i = 2;
      v = 2;
}
线程二: {
      if (v == 2) {
          System.out.println("t = " + i);
      }
}
在不使用 volatile 的情况下
明显,在单线程中 ,两个线程执行的方法体是没有相互依赖的
所以是可以重排序的,所以可以出现
线程一: {
v = 2
i = 2
}
线程二: {
// 先读取 i (即 先准备一下sout)
if(v == 2){
  把准备好的i输出
}
}

明显会出现
错误情况一: v = 2 , i = 1 导致 线程二输出 i = 1
错误情况二: v = 2 , i = 2 ,但是!!因为线程二可以准备好 i ,所以线程二输出 i = 1
都是错的
而使用 volatile 就可以避免这些情况

Q: 欸欸欸!凭什么show一定在set之后啊?就不能先show再set吗?

A:

标签:Java,show,关键字,volatile,内存,7.4,线程,public
From: https://www.cnblogs.com/deyo/p/17638577.html

相关文章

  • 【技术积累】Java 8 新特性
    一、Lambda表达式Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升1、举例(o1,o2)->Integer.compare(o1,o2)2、格式......
  • java中volatile关键字详解
    简介volatile是Java语言中的一种轻量级的同步机制,它可以确保共享变量的内存可见性,也就是当一个线程修改了共享变量的值时,其他线程能够立即知道这个修改。跟synchronized一样都是同步机制,但是相比之下,synchronized属于重量级锁,volatile属于轻量级锁。JMM概述JMM就是Java内存模型(Jav......
  • C语言中的关键字概述
    C语言的关键字是指具有特定功能的单词。可以使用关键字来帮助我们完成不同的事情。C语言最常见的关键字有32个,根据关键字的作用,可分为以下四类:一、数据类型关键字(共计12个):(1)char:声明字符型变量或函数返回值类型;(2)void:声明函数无返回值或无参数,声明无类型指针;(3)int: 声明整型变量或函数......
  • Android Java静态变量通信和反射的前提是须要在同一进程内
    静态变量通信:java类中的static变量是属于类的,即使new了两个对象访问的也是同一个内存地址的static变量,也就是说可以通过static变量通信,但前提必须是这两个对象必须是同一个进程中的。父进程通过fork来复制出一个子进程的副本,根据原理,子进程拥有父进程的一份完整数据拷贝。同时由......
  • 《Java编程思想第四版》学习笔记14
    //:Frog.java//TestingfinalizewithinheritanceclassDoBaseFinalization{publicstaticbooleanflag=false;}classCharacteristic{Strings;Characteristic(Stringc){s=c;out.println("Creating......
  • 《Java编程思想第四版》学习笔记15
    初始化的实际过程是这样的:(1)在采取其他任何操作之前,为对象分配的存储空间初始化成二进制零。(2)就象前面叙述的那样,调用基础类构建器。此时,被覆盖的draw()方法会得到调用(的确是在RoundGlyph构建器调用之前),此时会发现radius的值为0,这是由于步骤(1)造成的。(3)按照原先声明的......
  • 2023年 Java 面试八股文(20w字)
    第一章-Java基础篇1、你是怎样理解OOP面向对象难度系数:⭐面向对象是利于语言对现实事物进行抽象。面向对象具有以下特征:继承:继承是从已有类得到继承信息创建新类的过程封装:封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口多态性:多态性是指允许不同......
  • java stream分组之后求和
    javastream分组之后求和癞蛤蟆吃了小天鹅于2022-08-2609:37:42发布6023收藏4文章标签:java版权注:elementComponentDtos.stream().mapToDouble(ElementComponentDto::getAmount).sum();为求和可以根据返回类型的不同去改变相对应的求和函数(mapToDouble)注BigDecimal为了保......
  • 【狂神说Java】Java零基础学习笔记-JavaSE总结
    【狂神说Java】Java零基础学习笔记-JavaSE总结JavaSE总结:......
  • java:使用flexmark-java 实现 CommonMark(规范 0.28)解析
    文档https://github.com/vsch/flexmark-java依赖Java8<dependency><groupId>com.vladsch.flexmark</groupId><artifactId>flexmark-all</artifactId><version>0.62.2</version></dependency>Java9+&l......