volatile

volatile, synchronized, 读写屏障,CONCURRENTHASHMAP

血红的双手。 提交于 2019-12-01 12:07:54
Java 理论与实践: 构建一个更好的 HashMap ConcurrentHashMap 如何在不损失线程安全的同时提供更高的并发性 Brian Goetz , 首席顾问, Quiotix Corp Brian Goetz 是一名软件顾问,在过去的 15 年里,他一直是一名专业软件开发人员。他是 Quiotix 的首席顾问,Quiotix 是一家位于加尼福利亚州洛斯拉图斯(Los Altos)市的软件开发与咨询公司。请在业界流行的出版物中查阅 Brian 的 已出版和即将出版的文章 。 简介: ConcurrentHashMap 是 Doug Lea 的 util.concurrent 包的一部分,它提供比 Hashtable 或者 synchronizedMap 更高程度的并发性。而且,对于大多数成功的 get() 操作它会设法避免完全锁定,其结果就是使得并发应用程序有着非常好的吞吐量。这个月,Brian Goetz 仔细分析了 ConcurrentHashMap 的代码,并探讨 Doug Lea 是如何在不损失线程安全的情况下取得这么骄人成绩的。请在 讨论论坛 上与作者及其他读者共享您对本文的一些想法(也可以在文章的顶部或底部点击 讨论 来访问论坛)。 查看本系列更多内容 本文的标签: concurrenthashmap , hashmap , java , 构建一个更好的

java中锁的四种状态

淺唱寂寞╮ 提交于 2019-12-01 11:09:43
文章目录 前言 叙述 Synchronized volatile 锁的状态 锁是存在哪里的呢? 四种状态 锁状态转换过程 锁的优缺点 参考文章 小结 前言 在多线程并发编程中Synchronized一直是元老级角色,很多人都会称它为重量级锁,但是随着 Java SE1.6 对 Synchronized 进行了各种优化之后,有些情况下它并不那么重了,本文详细介绍了 Java SE1.6 中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁,以及锁的存储结构和升级过程。 叙述 首先我们以一张思维导图大概的了解一下锁状态,接下来将进行具体的阐述。 Synchronized synchronized,所谓的重量级锁。Java中每一个对象都可以作为一个锁,表现为: 对于普通方法的同步,锁是当前实例对象。 对于静态方法的同步,锁是当前类的Class对象。 对于同步方法块,锁是Synchronized括号里配置的对象。 JVM基于进入和退出Monitor对象来实现方法同步和代码同步。方法同步是使用monitorenter和monitorexit指令实现的,monitorenter指令是在编译后插入到同步代码块开始的位置,monitorexit是插在方法结束处和异常处。 volatile volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性

从 volatile 说到 i++ 的线程安全问题

左心房为你撑大大i 提交于 2019-12-01 10:42:17
volatile关键字保证了在多线程环境下,被修饰的变量在别修改后会马上同步到主存,这样该线程对这个变量的修改就是对所有其他线程可见的,其他线程能够马上读到这个修改后值. Thread的本地内存 每个Thread都拥有自己的线程存储空间 Thread何时同步本地存储空间的数据到主存是不确定的 例子 借用Google JEREMY MANSON 的解释,上图表示两个线程并发执行,而且代码顺序上为Thread1->Thread2 1. 不用 volatile 假如ready字段不使用volatile,那么Thread 1对ready做出的修改对于Thread2来说未必是可见的,是否可见是不确定的.假如此时thread1 ready泄露了(leak through)了,那么Thread 2可以看见ready为true,但是有可能answer的改变并没有泄露,则thread2有可能会输出 0 (answer=42对thread2并不可见) 2. 使用 volatile 使用volatile以后,做了如下事情 每次修改volatile变量都会同步到主存中 每次读取volatile变量的值都强制从主存读取最新的值(强制JVM不可优化volatile变量,如JVM优化后变量读取会使用cpu缓存而不从主存中读取) 在Thread中对volatile变量的写操作之前的操作对别的Thread都是可见的

指令重排序和内存屏障

百般思念 提交于 2019-12-01 10:22:23
一、指令重排序   指令重排序分为三种,分别为 编译器优化重排序 、 指令级并行重排序 、 内存系统重排序 。如图所示,后面两种为处理器级别(即为硬件层面)。 编译器优化重排序: 编译器在不改变程序执行结果的情况下,为了提升效率,对指令进行乱序的编译。例如在代码中A操作需要获取其他资源而进入等待的状态,而A操作后面的代码跟其没有依赖关系,如果编译器一直等待A操作完成再往下执行的话效率要慢的多,所以可以先编译后面的代码,这样的乱序可以提升不小的编译速度。 指令级并行重排序: 处理器在不影响程序执行结果的情况下,将多条指令重叠在一起执行,同样也是为了提升效率。 内存系统重排序: 这个跟之前两个不同的是,其为伪重排序,也就是说只是看起来像在乱序执行而已。对于现代的处理器来说,在CPU和主内存之间都具备一个高速缓存,高速缓存的作用主要为减少CPU和主内存的交互(CPU的处理速度要快的多),在CPU进行读操作时,如果缓存没有的话从主内存取,而对于写操作都是先写在缓存中,最后再一次性写入主内存,原因是 减少跟主内存交互时CPU的短暂卡顿 ,从而 提升性能 ,但是延时写入可能会导致一个问题——数据不一致。 // CPU1执行以下操作 a = 1; int i = b; // CPU2执行下面操作 b = 1; int j = a;其执行图如下:     从上面图中我们可以看到,对于CPU来说

简述JMM

断了今生、忘了曾经 提交于 2019-12-01 10:04:04
一、很多初学者分不清JMM和JVM的内存模型,本篇只是简要的谈一谈什么是JMM,并不深入探讨。   示意图A:      在多线程操纵共享资源时,并不是对资源本身进行的操作,而是将共享资源的副本复制了一份到自己的私有空间中,等使用完了再写回去覆盖原资源,我可能在瞎说,你先别信,举个例子来验证一下:    class Number{ int count = 0; public void add(){ this.count = 1; }}public class Demo2 { public static void main(String[] args) { Number number = new Number(); new Thread(() -> { System.out.println(Thread.currentThread().getName()+"***come in"); try{ TimeUnit.SECONDS.sleep(5); }catch (Exception e){ e.printStackTrace(); } number.add(); System.out.println(Thread.currentThread().getName()+" newCount: " + number.count); },"A").start(); new Thread(()

设计模式-单例模式

风流意气都作罢 提交于 2019-12-01 09:10:06
单例模式 饿汉式(急切实例化) public class EagerSingleton { /** 1.私有化构造方法 */ private EagerSingleton() { } /** 2.声明静态成员变量并赋初始值-类初始化的时候静态变量就被加载,因此叫做饿汉式 */ public static EagerSingleton eagerSingleton=new EagerSingleton(); /** 3.对外暴露公共的实例化方法 */ public static EagerSingleton getInstance(){ return eagerSingleton; } } 懒汉式(延迟实例化)(DCL双重检测)   -为什么要判断两次?      避免由于并发导致的线程安全问题   -为什么要使用 volatile关键字修饰 ourInstance?    避免指令重排序,同时保证变量ourInstance在线程之间是可见的 public class LazySingleton { /** 1.私有化构造方法 */ private LazySingleton() { } /** 2.声明静态变量 -这里声明为volatile,是禁止指令重排序 */ private volatile static LazySingleton ourInstance; /** 3

volatile 错误示范做线程同步 demo

浪尽此生 提交于 2019-12-01 08:18:39
这里 http://hedengcheng.com/?p=725 有对volatile 非常详细的解释,看完之后,心里一惊,因为我刚好在一个项目里用了文中错误示范那种方式来做线程同步,场景如下: Thread1 对性能要求非常高,它有一些中间数据需要定时同步给数据库,我就增加了一个线程Thread2 来帮它做(Thread2还干了很多其他事情),基本做法是:thread1 干完do_something后,给一个全局的 volatile flag 置1;Thread2在一个大循环里,当检测到flag值是1,则会去读thread1产生的中间数据并入库。 按照帖子的说法,flag=true 对应的汇编指令可能被编译器插入到 do_something 对应的指令中间,无法保证do_something做完值才变成1. 根据查找,发现gcc下有一个指令可以强制编译器和CPU 按顺序执行: #define BARRIER() do{ asm volatile("" ::: "memory");}while(0) 这样代码变成 Thread1() Thread2() {                    {   do_something... if(flag==true)   BARRIER; {   flag = true;  BARRIER; } other thing... } } 来源

C/C++ Volatile关键词深度剖析

别来无恙 提交于 2019-12-01 08:14:32
背景 此微博,引发了朋友们的大量讨论:赞同者有之;批评者有之;当然,更多的朋友,是希望我能更详细的解读C/C++ Volatile关键词,来佐证我的微博观点。而这,正是我写这篇博文的初衷:本文,将详细分析C/C++ Volatile关键词的功能 (有多种功能)、Volatile关键词在多线程编程中存在的问题、Volatile关键词与编译器/CPU的关系、C/C++ Volatile与Java Volatile的区别,以及Volatile关键词的起源,希望对大家更好的理解、使用C/C++ Volatile,有所帮助。 Volatile,词典上的解释为:易失的;易变的;易挥发的。那么用这个关键词修饰的C/C++变量,应该也能够体现出”易变”的特征。大部分人认识Volatile,也是从这个特征出发,而这也是本文揭秘的C/C++ Volatile的第一个特征。 Volatile:易变的 在介绍C/C++ Volatile关键词的”易变”性前,先让我们看看以下的两个代码片段,以及他们对应的汇编指令 (以下用例的汇编代码,均为VS 2008编译出来的Release版本): 测试用例一:非Volatile变量 b = a + 1;这条语句,对应的汇编指令是:lea ecx, [eax + 1]。由于变量a,在前一条语句a = fn(c)执行时,被缓存在了寄存器eax中,因此b = a + 1

C# 基础回顾: volatile 关键字

守給你的承諾、 提交于 2019-12-01 08:10:15
有些人可能从来没看到过这个关键字,这也难怪,因为这个关键字并不常用。那这个关键字到底有什么用呢? 我在网上搜索这个关键字的时候,发现很多朋友都有一个错误的认识 ------ 认为这个关键字可以防止并发争用(有点类似 lock 的赶脚)。 volatile 作用重定义 volatile 中文解释是“可变的”,MSDN 上关于此关键字的解释如下:“volatile 关键字指示一个字段可以由多个同时执行的线程修改。 声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。 这样可以确保该字段在任何时间呈现的都是最新的值。” 不知道你看了上述描述是不是恍然大悟,反正我是没看懂。在网上查阅了众多资料后,才算有所明白,把上面的话用新的方式重新解读后,就有了如下的结论。 1、阻止编译器优化:JIT 编译器会自动对代码进行优化,从而导致最终代码的指令顺序发生变化。使用 volatile 关键字就可以避免 JIT 编译器对此进行优化,如: public bool _goOn = true; //未优化 public void Execute() { while(_goOn) { //do something } } //优化后 public void ExecuteOptimized { if(_goOn) { while(true) { //do something } } }

volatile的用法

走远了吗. 提交于 2019-12-01 07:11:46
https://www.cnblogs.com/Herzog3/p/6203102.html 恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码: int i1; int geti1() {return i1;} volatile int i2; int geti2() {return i2;} int i3; synchronized int geti3() {return i3;}   geti1()得到存储在当前线程中i1的数值。多个线程有多个i1变量拷贝,而且这些i1之间可以互不相同。换句话说,另一个线程可能已经改 变了它线程内的i1值,而这个值可以和当前线程中的i1值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程 可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的i1值是1,线程1里的i1值 是2,线程2里的i1值是3——这在线程1和线程2都改变了它们各自的i1值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。    而geti2()得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主