JUC 学习(一) 之 volatile 关键字

news/2024/5/20 7:53:20 标签: Java, JUC, Thread

JUC 学习(一) 之 volatile 关键字

首先我们来看这么一段代码:

package indi.qiaolin.juc;

import lombok.Data;

/**
 *  测试JUC volatile 关键字
 *
 *  @author  qiaolin
 *  @version 2018年7月8日
 *
 */

public class VolatileTest {
    public static void main(String[] args) {
        ThreadDemo td = new ThreadDemo();
        new Thread(td).start();

        while(true){
            if(td.isFlag()){
                System.out.println("VolatileTest.main");
                break;
            }
        }
    }
}


@Data
class ThreadDemo implements Runnable{
    private boolean flag;

    public void run() {
        setFlag(true);
    }
}

你觉得最后的结果是不是控制台输出:VolatileTest.main 并且程序结束?

其实这个程序有80%的几率不会结束!陷入下面这个死循环!

while(true){
  if(td.isFlag()){ 
        System.out.println("VolatileTest.main");
        break;
    }
}

而陷入死循环的可能只有一种就是td这个对象的flag属性一直为false; 但是我们明明在td的run方法中设置成true了的啊!

public void run() {
   setFlag(true);
}

其实造成这个问题的情况就是 多个线程共享资源不可见的问题!每一个线程访问共享数据时都会先把数据读过来,然后如果有修改值的操作,就把改好的数据放回去!假设这个数据是在堆中!我们两个线程一个读一个写,那么这两个线程都是先把数据读到自己的缓存中,然后再操作,这时候如果A是读,B是写,那么如果A先读了数据,B后面去写,然后B把写好的数据放回到堆中,照理说A需要刷新他缓存中的数据,但是A如果处于一个高效率执行的情况下,例如 while(true) 他就会没时间去刷新或者刷新很慢这种情况,导致A所在的数据并不是已经被B修改过的数据,这个时候就出现了这个情况 内存可见性 !


下面是比较详细内存可见性的解释:
1、内存可见性(Memory Visibility)是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,需要确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化。
2、可见性错误是指当读操作与写操作在不同的线程中执行时,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。


来一套图说明下这个问题把!如果有错请提出来!多多交流指教!!

首先这两句代码创建了一个td对象, 并启动一个流程;我们暂且称这个流程为流程1

ThreadDemo td = new ThreadDemo();
new Thread(td).start();

这里写图片描述

流程1虽然已经启动,但是他肯定没有抢到执行权,此时main线程继续走,进入了这个while循环,但是while(true)的效率,那是相当的高,主要是他不用去调用其他对象或者运算!

 while(true){
     if(td.isFlag()){
          System.out.println("VolatileTest.main");
          break;
      }
  }

此时main 已经读了flag到自己的缓存
这里写图片描述

可能在执行中 线程1抢到了执行权
这里写图片描述
这里写图片描述
这里写图片描述
他先读取了flag数据,然后修改,然后写回去!

但是main线程在高效率的情况下,他一直猛干,根本就不去刷新,拿到的还是缓存的值 就导致了main线程中的td.isFlag()始终是false!
不过我运行这个程序多次,我发现有时候还是会去刷新的,并不是百分百的会死循环!但是有一定的几率!于是我又做了个实验,让他这个代码变得慢一点循环!

 while(true){
    if(td.isFlag()){
        System.out.println("VolatileTest.main");
        break;
    }
    System.out.println("main");
}

加了一句输出,你就会发现,他居然没那么容易死循环了!
这里写图片描述

但原因并不是效率太高的问题,而是他们两个线程不可见的问题,那我们每次都要多加上几句其他代码来解决? 这肯定不是解决方案啊!这么low,怎么阔能!
解决方案有两种:
1、加上 synchronized 关键字,这个同步关键字会刷新当前线程的缓存!
2、我们只需要把多个线程共享的数据使用volatile关键字, volatile 变量,用来确保将变量的更新操作通知到其他线程。可以将volatile 看做一个轻量级的锁,但是又与锁有些不同;对于多线程,不是一种互斥关系(当前所对象只能进来一个)但是又不能保证变量状态的“原子性操作”

如果你只是想让一个属性多个线程可见的话就用 volatile把!
例:

@Data
class ThreadDemo implements Runnable{
    private volatile boolean flag;

    public void run() {
        setFlag(true);
    }
}

http://www.niftyadmin.cn/n/1267486.html

相关文章

Linux 学习之 VMTools安装

VMTools安装 1、介绍 ​ vmTools的好处就直接在虚拟机和windows之间使用复制粘贴,并且还可以设置windows和centOs的共享文件夹! ​ ​ 2、安装步骤 1.进入到centOs系统 2.点击vm虚拟机 –> 安装WMware Tools ​ 3.centOS会出现一个wm的安…

Linux 学习之 Linux 目录介绍

Linux 目录介绍 1、基本介绍 ​ linux 的文件系统是采用级层式的树状目录结构,在此结构中的最上层是根目录 ‘’/‘’ ,然后在此目录下在创建其他的目录。这一点和windows是不同的,windows会有多个根目录!像c盘就是一个根目录&#xff0…

Linux 学习之 vi 和 vim 编辑器

vi 和 vim 编辑器 1、介绍 ​ 所有的Linux系统都会自带vi 文本编辑器,相当于windows的记事本。 ​ Vim 具有程序编辑能力,可以看作是 vi 文本编辑器的增强版本,可以主动的以字体颜色辨别语法的正确性,方便程序设计。代码补全…

Linux 学习之 开机、重启、用户注销

开机、重启、用户注销 1、关机和重启命令介绍 命令说明shutdown –h now立该进行关机shutdown -h 11分钟之后关机,1可以随机定值shutdown –r now现在重启shutdown -r 11分钟之后重启,1可以随机定值halt关机reboot现在重新启动计算机sync把内存的数据同…

Linux 学习之 用户、用户组管理

用户、用户组管理 1、用户介绍 ​ 1、在linux中每个用户都有自己的家目录,也就是每次登陆成功进入的目录,新增用户时如果不指定家目录,linux会在 /home/ 下建立一个与用户名(id)相同的名称的家目录。 ​ 2、在linux中一个用户必须属于一个…

Linux 学习之 Linux 运行级别

Linux 运行级别 1、介绍 ​ Linux 下分了多种运行级别,所谓的运行级别就是当前系统启动后能做什么! ​ 运行级别如下: ​ 级别说明0关机1单用户【可以找回丢失密码】2多用户状态但是没有网络3多用户有网络服务4系统未使用&#xff0c…

Linux 学习之 帮助指令

Linux帮助指令 1、基本介绍 ​ 当我们对某个指令不熟悉的时候,我们可以使用Linux提供的帮助指令来了解这个命令的用法! 2、指令用法 2.1 man 指令 2.1.1 基本语法 ​ man 指令名 2.1.2 实际案例 ​ ​ 如果不想查看了,按下 q …

find: 遗漏“-exec”的参数 解决方案

##find: 遗漏“-exec”的参数 解决方案 例句: find /var/ -mtime 10 -name “*.tar.gz” -exec rm -rf {} ; 出现这个问题很有可能是: 1、{} 和 \ 之间有空格 2、\ 和 ; 之间没有空格! 正确的写法: {} ;