java内存模型(jMM)(二)
volatile关键字
volatile是一个类型修饰符(type specifier),就像大家更熟悉的const一样,它是被设计用来修饰被不同线程访问和修改的变量。volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
特点:
- 其他线程对变量的修改可以立即反应到当前线程中
- 确保当前线程对volatile变量的修改,能即时的写到共享内存中,并被其他线程所见
- 使用volatile修饰的变量,编译器会保证其有序性
正确使用volatile变量的条件:
- 对变量的写操作不依赖于当前值
- 该变量没有包含在具有其他变量的表达式中
第一个条件显示volatile变量不适合用作线程安全计数器,因为在多线程中,增量操作(count++)虽然看上去是一个单独的操作,但实际上是由读取-->修改-->写入操作组成的组合操作,volatile并不能保证整个过程都是原子操作。
适用场景:
状态标志
这种类型的状态标记的一个公共特性是:通常只有一种标记状态的来回转换,而且状态变量通常是原子变量
- 一次性安全发布
缺乏同步会导致无法实现可见性,这使得确定何时写入对象引用而不是原语值变得更加困难,此时我们可以利用volatile实现对象的安全发布。这种模式的一个必要条件是:被发布的对象必须是线程安全的,或者是有效的不可变对象(有效不可变意味着对象的状态在发布后不会被修改)。volatile类型的引用可以确保对象的发布形式的可见性,但是如果对象的状态在发布后发生改变,那么需要额外的同步
- 独立观察
安全使用volatile的另一种简单模式是:定期“发布”观察结果供程序内部使用。使用该模式的另一种应用程序是收集程序的统计信息,例如记录一个程序中最后登录的用户,可以将反复使用lastUser引用来发布值,以便程序的其他部分使用。这个模式要求被发布的值是有效不可变的--即值的状态在发布后不会改变。
- “volatile bean”模式
volatile bean 模式适用于将 JavaBeans 作为“荣誉结构”使用的框架。在 volatile bean 模式中,JavaBean 被用作一组具有 getter 和/或 setter 方法 的独立属性的容器。volatile bean 模式的基本原理是:很多框架为易变数据的持有者(例如 HttpSession)提供了容器,但是放入这些容器中的对象必须是线程安全的。
在 volatile bean 模式中,JavaBean 的所有数据成员都是 volatile 类型的,并且 getter 和 setter 方法必须非常普通 —— 除了获取或设置相应的属性外,不能包含任何逻辑。此外,对于对象引用的数据成员,引用的对象必须是有效不可变的。(这将禁止具有数组值的属性,因为当数组引用被声明为 volatile 时,只有引用而不是数组本身具有 volatile 语义)。对于任何 volatile 变量,不变式或约束都不能包含 JavaBean 属性。
- 开销较低的读-写锁策略
当读操作远远超过写操作时,我们可以结合使用内部锁和volatile变量来减少公共代码路径的开销。如果更新不频繁的话,该方法可以实现更好的性能,因为读路径的开销仅仅涉及到volatile的读操作,这通常要由于一个无竞争的锁的开销。
public class ChessyCounter{
private volatile int count;
public int getValue(){return this.count;}
public synchronized int increment(){
return count++;
}
}
之所以将这种技术称之为“开销较低的读-写锁”,是因为使用的不同的同步机制进行读写操作。由于在读操作中使用volatile可以确保当前值的可见性,所以可以使用锁进行所有变化的操作,使用volatile进行只读操作。
总结:
与锁相比,Volatile 变量是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性。如果严格遵循 volatile 的使用条件 —— 即变量真正独立于其他变量和自己以前的值 —— 在某些情况下可以使用 volatile 代替 synchronized 来简化代码。然而,使用 volatile 的代码往往比使用锁的代码更加容易出错。本文介绍的模式涵盖了可以使用 volatile 代替 synchronized 的最常见的一些用例。遵循这些模式(注意使用时不要超过各自的限制)可以帮助您安全地实现大多数用例,使用 volatile 变量获得更佳性能。
synchronized关键字
synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。它包括两种用法:synchronized 方法和 synchronized 块。
特点:
- 被synchronized修饰的方法或代码块同一时间只能被一个线程使用
- 当一个线程访问Object的一个synchronized代码块时,另一个线程仍可访问该对象的非synchronized代码块
- 当一个线程访问Object的一个synchronized代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
- 当一个线程访问Object的一个synchronized(this)同步代码块时,它就获得了这个Object的对象锁。结果,其他线程对该Object对象所有的同步代码部分的访问都暂时被阻塞
使用场景:
- synchronized方法:
通过在方法声明中加入synchronized关键字,synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)
- synchronized代码块:
通过 synchronized关键字来声明synchronized 块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
- synchronized方法和synchronized同步代码块的区别:
- synchronized同步代码块只是锁定了该代码块,代码块外面的代码还是可以被访问的。
- synchronized方法是粗粒度的并发控制,某一个时刻只能有一个线程执行该synchronized方法。
- synchronized同步代码块是细粒度的并发控制,只会将块中的代码同步,代码块之外的代码可以被其他线程同时访问。
final关键字
特点:
- final类不能被继承,没有子类,final类中的方法默认是final的。
- final方法不能被子类的方法覆盖,但可以被继承。
- final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
- final不能用于修饰构造方法。
使用场景:
- 修饰变量
当final修饰一个变量的时候一般把他作为常量,通常和static关键字配合使用。一般static修饰的常量都用大写字母来表示。
- 修饰方法
当一个方法被final修饰后,表示该方法不能被子类重写,final方法有一个好处是比非final方法要快,因为在编译时已经静态绑定了,不需要在运行时在动态绑定。
- 修饰类
当一个类被final修饰后,表示该类是完整的,不能被继承,例如Java中String、Integer类都是final类
优点:
- final关键字提高了性能。JVM和Java应用都会缓存final变量
- final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销
- 使用final关键字,JVM会对方法、变量及类进行优化
- 不可变类创建不可变类要使用final关键字。不可变类是指它的对象一旦被创建了就不能被更改了。String是不可变类的代表。不可变类有很多好处,譬如它们的对象是只读的,可以在多线程环境下安全的共享,不用额外的同步开销等等
参考资料:
java内存模型(jMM)(二)的更多相关文章
- Java内存模型JMM与可见性
Java内存模型JMM与可见性 标签(空格分隔): java 1 何为JMM JMM:通俗地讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这 ...
- 多线程并发之java内存模型JMM
多线程概念的引入是人类又一次有效压寨计算机的体现,而且这也是非常有必要的,因为一般运算过程中涉及到数据的读取,例如从磁盘.其他系统.数据库等,CPU的运算速度与数据读取速度有一个严重的不平衡,期间如果 ...
- 全面理解Java内存模型(JMM)及volatile关键字(转载)
关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...
- 全面理解Java内存模型(JMM)及volatile关键字(转)
原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型( ...
- 对多线程java内存模型JMM
多线程概念的引入体现了人类重新有效压力寨计算机.这是非常有必要的,由于所涉及的读数据的过程中的一般操作,如从磁盘.其他系统.数据库等,CPU计算速度和数据读取速度已经严重失衡.假设印刷过程中一个线程将 ...
- Java并发编程:Java内存模型JMM
简介 Java内存模型英文叫做(Java Memory Model),简称为JMM.Java虚拟机规范试图定义一种Java内存模型来屏蔽掉各种硬件和系统的内存访问差异,实现平台无关性. CPU和缓存一 ...
- Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)
JVM运行时内存结构回顾 在JVM相关的介绍中,有说到JAVA运行时的内存结构,简单回顾下 整体结构如下图所示,大致分为五大块 而对于方法区中的数据,是属于所有线程共享的数据结构 而对于虚拟机栈中数据 ...
- java内存模型(二)深入理解java内存模型的系列好文
深入理解java内存模型(一)--基础 深入理解java内存模型(二)--重排序 深入理解java内存模型(三)--顺序一致性 深入理解java内存模型(四)--volatile 深入理解java内存 ...
- 什么是Java内存模型(JMM)
什么是java内存模型 缓存一致性问题 在现代计算机中,因为CPU的运算速度远大于内存的读写速度,因此为了不让CPU在计算的时候因为实时读取内存数据而影响运算速度,CPU会加入一层缓存,在运算之前缓存 ...
- 深入理解Java内存模型JMM与volatile关键字
深入理解Java内存模型JMM与volatile关键字 多核并发缓存架构 Java内存模型 Java线程内存模型跟CPU缓存模型类似,是基于CPU缓存模型来建立的,Java线程内存模型是标准化的,屏蔽 ...
随机推荐
- 超出div宽度范围的文字进行省略号省略,在鼠标移上去以后显示完整的内容
一.前言 当我们在固定的范围内显示内容时,我们是希望能够完整显示的,然而往往事与愿违,文本会超出我们给定的范围,这时候怎么办呢? 二.超出范围,对文本进行省略号隐藏 先上图 代码很简单 div{ wi ...
- 软件测试流程(Test Flow)
Bug Status Definition Bug Management Process Outline Bug Severity&Priority Criteria Definition
- linux fuser的使用
当进行共享存储的时候,umount可能无法用于卸载某个设备,说是被某个进程所占用,但是又无法找到该进程.这个时候使用fuser -km /data命令杀死所有在使用这个存储设备的进程然后再umount ...
- vs2017编译并配置libcurl入门教程
libcurl可以用来发送http请求,是c/c++发送http请求常用的库 下载libcurl源码包: libcurl下载地址:https://curl.haxx.se/download.html ...
- [原创]SOUI GDI+渲染引擎下的字体特效,抛砖引玉
由于SOUI是一种双渲染引擎的DUI库,默认在SKIA渲染引擎下是支持特效字体的,具体请参考DEMO中的源码. 但是使用GDI+渲染时是没有这些特效的,着实比较苦恼,此代抛砖引玉,细节实现 请自己再去 ...
- Mybatis:传入参数方式以及#{}与${}的区别
一.在MyBatis的select.insert.update.delete这些元素中都提到了parameterType这个属性.MyBatis现在可以使用的parameterType有基本数据类型和 ...
- python 学习之路开始了
python 学习之路开始了.....记录点点滴滴....
- es学习-索引配置
1.创建一个新的索引并且添加一个配置 2.更新索引配置:(更新分词器为例子) 更新分词器前,一定要关闭索引,然后更新,最后再次开启索引 url:PUT http://127.0.0.1:9200/su ...
- 样条曲线catmull rom转bezier
b0,..,b3是贝塞尔,c-1, c2是catmull rom控制点 [b0] = 1 [ 0 6 0 0] [c_1] [b1] - [-1 6 1 0] [c0] [b2] 6 [ 0 1 6 ...
- ceph中查看一个rbd的image的真实存储位置
1.新建一个image存储 rbd create hzb-mysql --size 2048 2.查看hzb-mysql的所有对象 一个rbd image实际上包含了多个对象(默认情况下是image_ ...