Volatile关键字回顾之线程可见性

java中,volatile关键字有两大作用:

  1.保证线程的可见性

  2.防止指令重排序

这篇文章主要通过典型案例,体现可见性这一特性。

概念:

  java中,堆内存是线程共享的。而每个线程,都应该有自己独享的内存用于工作。所以,当线程访问到堆内存中的共享变量时,会考虑copy一份,存在自己的工作空间中。之后对工作空间中的值进行操作,完成后重新写回到堆内存。

  这样一来,在多线程同时访问这个变量时,就可能出现拿不到实时的值的问题(根本原因在于:本来共享的变量,因为缓存到各个线程的工作空间导致数据在写回之前不互通)。

  而volatile的作用之一,就是解决这一问题。

下面通过一段代码,来展示volatile的这一特性。

 1 public class HelloVolatile {
 2     private volatile boolean running = true; //开关,表示是否可运行
 3
 4     void m(){
 5         System.out.println("m start");
 6         //一直执行
 7         while(running){
 8
 9         }
10         //如果running一直未true,下面的代码执行不到
11         System.out.println("m end");
12     }
13
14     public static void main(String[] args) {
15         HelloVolatile helloVolatile = new HelloVolatile();
16         //新建线程,run方法调用m方法,running为true时一直循环
17         new Thread(helloVolatile::m, "非主线程").start();
18         //休眠1s
19         try {
20             TimeUnit.SECONDS.sleep(2);
21         } catch (InterruptedException e) {
22             e.printStackTrace();
23         }
24         //主线程设置running为false,如果running不加volatile,则新建的线程是读不到的
25         helloVolatile.running = false;
26     }
27 }

running变量不加volatile的执行结果:

m start

加了volatile的执行结果:

m start
m end

可见性的原理:

MESI(缓存一致性)协议:

  1.写数据时,如果发现操作的是共享变量,会发出信号通知其他CPU设置该变量的缓存行为无效状态。

  2.当其他CPU使用这个变量时,会先去观察是否有更改的信号,当发现缓存行失效时,会从主内存(堆中)重新读取此变量。
volatile的优缺点:

  优点:高效(非锁机制)

  缺点:仅仅保证可见性,不保证数据的一致性。

原文地址:https://www.cnblogs.com/nysd/p/12547703.html

时间: 2024-07-29 22:02:52

Volatile关键字回顾之线程可见性的相关文章

volatile关键字、原子性和可见性

1.volatile关键字 理解volatile的关键首先要理解处理器缓存和主存. 如果将一个域声明为volatile,那么只要对这个域产生了写操作,那么所有读操作都可以看到这个修改,即volatile域的写操作会向主存刷新. 同步synchronized也会导致向主存中刷新,所以如果一个域完全由synchronized保护就不必设置为volatile. 2.原子性和可见性 可见性: 原子性:

volatile关键字能否保证线程安全?

单纯使用 volatile 关键字是不能保证线程安全的 volatile 只提供了一种弱的同步机制,用来确保将变量的更新操作通知到其他线程 volatile 语义是禁用 CPU 缓存,直接从主内存读.写变量.表现为:更新 volatile 变量时,JMM 会把线程对应的本地内存中的共享变量值刷新到主内存中:读 volatile 变量时,JMM 会把线程对应的本地内存设置为无效,直接从主内存中读取共享变量 当把变量声明为 volatile 类型后,JVM 增加内存屏障,禁止 CPU 进行指令重排

Java中的volatile关键字

一.计算机内存模型的相关概念 计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,可能会涉及到数据的读取和写入.由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢得多,因此如果任何时刻对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度,因此自CPU里面就有了高速缓存. 也就是,当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU高速缓存中,那么CPU进行计

Java多线程编程——volatile关键字

(本篇主要内容摘自<Java多线程编程核心技术>) volatile关键字的主要作用是保证线程之间变量的可见性. package com.func; public class RunThread extends Thread{ private boolean isRunning = true; // volatile private boolean isRunning = true; public boolean isRunning() { return isRunning; } public

全面理解Java内存模型(JMM)及volatile关键字(转)

原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入理解Java并发之synchronized实现原理 Java并发编程-无锁CAS与Unsafe类及其并发包Atomic 深入理解Java内存模型(JMM)及volatile关键字 剖析基于并发AQS的重入锁(Reetr

Volatile 关键字浅析解析

1. volatile的定义Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致性地更新,线程应该确保通过排他锁单独获取这个变量.Java语言提供了volatile,在某些情况下比锁更加方便.如果一个字段被声明成volatile关键字,Java线程内存模型确保所有线程看到这个变量值的一致性.2.volatile的实现原则 1)Lock前缀指令会引起处理器缓存写回内存.Lock前缀指令导致在执行指令期间,声言处理器的Lock#信号.在多核处理器环境中,Lock#信号确保在声言该信号

双重检查锁单例模式为什么要用volatile关键字?

前言 从Java内存模型出发,结合并发编程中的原子性.可见性.有序性三个角度分析volatile所起的作用,并从汇编角度大致说了volatile的原理,说明了该关键字的应用场景:在这补充一点,分析下volatile是怎么在单例模式中避免双检锁出现的问题的. 并发编程的3个条件 1.原子性:要实现原子性方式较多,可用synchronized.lock加锁,AtomicInteger等,但volatile关键字是无法保证原子性的:2.可见性:要实现可见性,也可用synchronized.lock,v

内存栅栏和volatile关键字

内存栅栏和volatile关键字 前言 本次主要讲解关于内存栅栏的一点小东西,主要是扫盲,给大家普及普及概念性的东西.以前我们说过在一些简单的案例中,比如一个字段赋值或递增该字段,我们需要对线程进行同步. 虽然lock可以满足我们的需要,但是一个竞争锁一定会导致阻塞,然后忍受线程上下文切换和调度的开销.有些高并发和性能比较关键的地方,这些是不能忍受的. .net提供了非阻塞同步构造,为一些简单的操作提高了性能,它甚至都没有阻塞,暂停,和等待线程. 引入 Memory Barries and Vo

volatile关键字与内存可见性

前言 首先,我们使用多线程的目的在于提高程序的效率,但是如果使用不当,不仅不能提高效率,反而会使程序的性能更低,因为多线程涉及到线程之间的调度.CPU上下文的切换以及包括线程的创建.销毁和同步等等,开销比单线程大,因此需谨慎使用多线程. 在jdk1.5以后,提供了一个强大的java.util.current包,这个包中提供了大量的应用于线程的工具类. 下面开始介绍volatile关键字和内存可见性,虽然volatile是在jdk1.5之前就有的,但还是想放在这里讲一下. 举例说明 首先,我们先看