本篇文章为你整理了volatile的实现原理,源码案例深度剖析! – mikechen的互联网架构()的详细内容,包含有 volatile的实现原理,源码案例深度剖析! – mikechen的互联网架构,希望能帮助你了解 volatile的实现原理,源码案例深度剖析! – mikechen的互联网架构。
系统掌握 Java 多线程和并发编程的技术原理和知识点,写出优秀的并发代码
熟练应用各种并发工具,了解在什么情况下使用哪些具体的技术和方法
分掌握常见的多线程和并发问题分析技巧,知道排查一般问题的具体步骤
构建完整全面的并发编程知识体系,以及常见的面试问题和技巧
精通Java并发编程,彻底掌握 Java 并发编程知识。
详细内容
操作系统的发展历程
多线程、纤程、协程
硬件内存架构
并发和并行
CPU和高速缓存
缓存一致性
指令重排
支撑Java内存模型原理
Volatile的实现源码剖析
内存屏障
经典的单例的双重检测源码剖析
AQS的设计和结构
AQS源码深度剖析
线程通信与状态流转
Synchronized的实现原理及应用
Synchronized锁的膨胀升级过程分析
乐观锁、悲观锁、重入锁、公平锁、非公平锁及锁的粒度详解
ReentrantLock源码深度剖析与实战
可重入锁原理、获取锁和释放锁
ReentrantReadWriteLock源码深度剖析与实战
Condition 条件队列
线程池核心原理
线程池核心参数、拒绝策略、任务流程详解
线程池ThreadPollExecutor
阻塞与非阻塞队列详解
CountDownLatch源码深度剖析与实战
Semaphore源码深度剖析与实战
CyclicBarrier源码深度剖析与实战
ArrayBlockingQueue源码深度剖析
ConcurrentLinkedQueue 源码深度剖析
PriorityBlockingQueue 源码深度剖析
并发Atomic原子
死锁解决方案
CAS算法乐观锁
CAS的ABA问题
ThreadlLocal源码深度解析
ThreadlLocal数据隔离
ThreadlLocal内存溢出
ForkJoin原理解析
ConcurrentHashMap JDK1.8源码剖析
ArrayList、LinkedList、CopyOnWriteArrayList的实现原理
阻塞队列的实现原理与应用
非阻塞队列的实现原理与应用
ConcurrentLinkedQueue源码深度剖析
ArrayBlockingQueue源码深度剖析
深入LinkedBlockingQueue实现原理
反射泛型
从 0 掌握消息队列(MQ)的关键技术,了解核心知识
全面了解各类 MQ 技术的原理和特性,洞悉相关原理
深入理解 MQ 的特点和应用场景
掌握RabbitMQ RocketMQ Kafaka架构设计
全面吃透RocketMQ的源码底层实现
详细内容
消息队列的设计
消息队列的核心组成
消息队列的传输模式
消息队列的消费模式
消息队列的消息协议
消息队列的发送方式
消息队列的应用
异步调用的实现原理
应用解耦的实现原理
削峰填谷的实现原理
消息队列的架构与选型
Kafka的核心架构设计
RabbitMQ的核心架构设计
RocketMQ的核心架构设计
主流消息队列的选型与优劣比较
如何设计一个消息队列
消息队列的整体架构
消息队列的核心流程
消息队列传输过程
消息队列如何数据存储
消息队列如何做消息消费
同步异步编程
同步编程的实现
异步编程的实现
Future的源码剖析
同步、异步源码案例讲解
RocketMQ零拷贝
RocketMQ Mmap的实现
PageCache
Mmap的底层实现原理
虚拟内存
缺页中断
RocketMQ源码深度剖析
RocketMQ异步通信
RocketMQ核心存储
RocketMQ消费队列
精通分布式事务并对其原 理有深入理解
精通分布式锁、Session、全局唯一ID等并对其原 理有深入理解
掌握分布式数据库并对原理有深入理解
全面了解分布式的协议
全面了解分布式存储方案
详细内容
微服务架构变迁史
淘宝分布式架构演变过程
分布式协议
一致性模型
Gossip协议
Paxos协议
Raft协议
Zab协议
分布式Session解决方案
session
分布式session
分布式session方案
Session复制
Session存储在Cookie
Session粘性管理
Session集中管理在后端
分布式Session方案优劣势比较
分布式事务解决方案
分布式事务
BASE
一致性模型
XA两阶段
事务补偿TCC
消息队列最终一致性
分布式锁解决方案
分布式锁的由来
分布式锁的特点
分布式锁解决方案
数据库分布式锁
Redis分布式锁
Zookeeper分布式锁
分布式锁解决方案优劣势比较
分布式全局唯一ID
分布式全局唯一ID的要求
分布式全局唯一ID的方案
分布式全局唯一ID方案的优劣势比较
Snowflake雪花算法详解
大厂分布式全局唯一ID方案
分布式关系SQL数据库解决方案
SQL - NoSQL- NewSQL发展轨迹
MySQL+分库分表
Spanner
Aurora
NewSQL新型分布式数据库比较
分布式NoSQL数据库解决方案
NoSQL的三大基石
列式数据
文档数据库
图形数据库
内存键值数据库
主流NoSQL数据库比较
分布式文件存储解决方案
FastDFS
MogileFS
MooserFS
GlusterFS
Ceph
14:亿级高性能架构设计
上一节讲到Java内存模型用来屏蔽不同硬件和操作系统的内存访问差异,期望Java程序在各种平台上都能实现一致的内存访问效果。
而具体在Java内存模型里是如何来解决内存数据一致性的问题呢?
这就不得不谈到今天的核心关键点:Volatile的实现原理。
为了助大家掌握好Volatile,本节课重点会讲到以下5点:
1.Volatile关键字
2.Java内存模型
3.Volatile内存模型可见性
4.Volatile的工作原理
5.Volatile的源码案例
在谈Volatile之前,我们先回顾下Java内存模型的三要素:原子性、可见性、有序性,也就是大家常提到的并发编程三要素。
并发编程的三要素Volatile的内存模型Volatile的实现原理Volatile源码案例
并发编程的三要素
1.原子性
和数据库事务中的原子性一样,满足原子性特性的操作是不可中断的,要么全部执行成功要么全部执行失败
只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。
比如:i = 2;j = i;i++;i = i + 1;
上面4个操作中,i=2是读取操作,必定是原子性操作,j=i你以为是原子性操作,其实吧,分为两步,一是读取i的值,然后再赋值给j,这就是2步操作了,称不上原子操作,i++和i = i + 1其实是等效的,读取i的值,加1,再写回主存,那就是3步操作了。
所以上面的举例中,最后的值可能出现多种情况,就是因为满足不了原子性。
非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作,java的concurrent包下提供了一些原子类:比如:AtomicInteger、AtomicLong等。
2.可见性
多个线程访问同一个共享变量时,其中一个线程对这个共享变量值的修改,其他线程能够立刻获得修改以后的值
3.有序性
编译器和处理器为了优化程序性能而对指令序列进行重排序,也就是你编写的代码顺序和最终执行的指令顺序是不一致的。
但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
Volatile
Volatile 是一个Java语言的类型修饰符,一旦一个共享变量(类的成员变量、类的静态成员变量)被Volatile修饰之后,那么就具备了两层语义:
1、保证多线程下的可见性
2、禁止进行指令重排序(即保证有序性)
这里需要注意一个问题,Volatile只能让被他修饰内容具有可见性、有序性。
Volatile只能保证对单次读/写的原子性,i++ 这种操作不能保证原子性。
Volatile的内存模型
Java 内存模型(JMM)是一种抽象的概念,并不真实存在,它描述了一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段、静态字段和构成数组对象的元素)的访问方式。
试图屏蔽各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。
Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。
主内存主要存储的是Java实例对象,所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量还是方法中的本地变量(也称局部变量),当然也包括了共享的类信息、常量、静态变量。由于是共享数据区域,多条线程对同一个变量进行访问可能会发现线程安全问题。
工作内存每条线程都有自己的工作内存(Working Memory,又称本地内存,可与前面介绍的处理器高速缓存类比),线程的工作内存中保存了该线程使用到的变量的主内存中的共享变量的副本拷贝。工作内存是 JMM 的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
主要存储当前方法的所有本地变量信息(工作内存中存储着主内存中的变量副本拷贝),每个线程只能访问自己的工作内存,即线程中的本地变量对其它线程是不可见的,就算是两个线程执行的是同一段代码,它们也会各自在自己的工作内存中创建属于当前线程的本地变量,当然也包括了字节码行号指示器、相关Native方法的信息。
Volatile的实现原理
Volatile 保证内存可见性
主内存和工作内存之间的交互有具体的交互协议,JMM定义了八种操作来完成,这八种操作是原子的、不可再分的,它们分别是:lock,unlock,read,load,use,assign,store,write,其中lock,unlock,read,write作用于主内存;load,use,assign,store作用于工作内存。
(1) lock:将主内存中的变量锁定,为一个线程所独占
(2) unclock:将lock加的锁定解除,此时其它的线程可以有机会访问此变量
(3) read:将主内存中的变量值读到工作内存当中
(4) load:将read读取的值保存到工作内存中的变量副本中。
(5) use:将值传递给线程的代码执行引擎
(6) assign:将执行引擎处理返回的值重新赋值给变量副本
(7) store:将变量副本的值存储到主内存中。
(8) write:将store存储的值写入到主内存的共享变量当中。
从主存复制变量到当前工作内存(read and load)
执行代码,改变共享变量值 (use and assign)
用工作内存数据刷新主存相关内容 (store and write)
指令规则
read 和 load、store和write必须成对出现
assign操作,工作内存变量改变后必须刷回主内存
同一时间只能运行一个线程对变量进行lock,当前线程lock可重入,unlock次数必须等于lock的次数,该变量才能解锁。
对一个变量lock后,会清空该线程工作内存变量的值,重新执行load或者assign操作初始化工作内存中变量的值。
unlock前,必须将变量同步到主内存(store/write操作)
Volatile源码案例
package com.yzxy.concurrent.basic;
* 加与不加volatile
* 不加volatile:main线程中将isRunning设置为flase,VolatileDemo线程中的isRunning不会改变
* 加上volatile:main线程中将isRunning设置为flase,VolatileDemo线程中的isRunning会随之改变
private boolean isRunning = true;
volatileDemo.setRunning(false);
System.out.println("isRunning的值已经设置为false...");
while(isRunning){
//如果VolatileDemo线程的isRunning不改为false,线程会永远卡在这里
System.out.println( running ) //加上这句话就不会死循环,怎么解释
}
这个问题我记得面试辅导的时候就问过,面试辅导的时候回复了你,怎么还卡在这里。
既然你发现了System.out.println(“running”) 加上这句话就不会死循环(volatile内存可见性失效),那解决的思路就很简单了:
肯定是System.out.println这个方法让内存发生了可见性(替换了volatile的作用)。
所以,解决思路就来了:看看println究竟做了什么事情。
你只需要点一下println(进入源码),答案就放在那里。
public class VolatileTest extends Thread {
private boolean flag = false;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
while (!flag){
// System.out.println(1);
}
System.out.println( 线程停止 );
}
public static void main(String[] args) throws InterruptedException {
VolatileTest test = new VolatileTest();
test.start();
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
test.setFlag(true);
}
}).start();
}
}
while循环体中,执行System.out.println方法, flag没有使用volatile修饰,线程2修改flag变量,代码正常退出
执行main方法控制台打印如下
1
1
1
1
1
线程停止
valatile在锁的实现上比如ReentrantLock中使用过,主要用来保证变量在多线程访问的情况下的可见性和有序性。volatile通过内存屏障来实现,在对变量的load和store时会增加内存屏障,内存屏障指令会告知jvm编译器和cpu,禁止前后的指令进行重新排序,保证了有序性;另外在store后的store屏障会立即将工作内存中的变量值刷新到主内存中,保证了可见性。
volatile具有修改可见性(一个线程去修改值,别的线程是可见的),不具有原子性
volatile的适用场景;状态标志、一次性安全发布、独立观察、“volatile bean” 模式、开销较低的“读-写锁”策略
volatile的实现原理:
vilatile可见性的实现是借助了CPU的lock指令,通过在写volatile的机器指令钱加上lock前缀,
使写voletile具有以下两个原则:
1、写volitile时处理器会将缓存写回到主内存
2、一个处理器的缓存写回到内存会导致其他处理器的缓存失效。
volitile有序性的保证是通过禁止指令重排序来实现的(内存屏蔽)
每一次作业的输出,都是一次非常好的面试的演练,其实就是一次线下面试的模拟演练。
这里给到一个我的建议:可以先输出,输出前用语言的方式来表达出来,比如:我如果用语言的方式来表达,我应该怎么来回答volatile的实现原理,是否可以先抓住最核心的重点来切入:可见性与有序性,然后分别从可见性与有序性来解答这个角度,面试官一般给到的时间非常短(1-2分钟),能快速说重点,再从重点来阐述,这个角度面试官对你的印象就会非常好。所以,作业的输出,你完全可以按照线下面试的角度来(提前模拟面试),不断练习就好了,希望这一点对你有所帮助,@路正银 加油 ✗拳头✗
言简意赅,volatile的底层实现原理还可以从可见性与有序性的角度,画出对应的技术原理图 ,这样会更有助于理解 ✗咧嘴笑✗
以上就是volatile的实现原理,源码案例深度剖析! – mikechen的互联网架构()的详细内容,想要了解更多 volatile的实现原理,源码案例深度剖析! – mikechen的互联网架构的内容,请持续关注盛行IT软件开发工作室。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。