java并发编程实战:第十六章----Java内存模型
一、什么是内存模型,为什么要使用它
如果缺少同步,那么将会有许多因素使得线程无法立即甚至永远看到一个线程的操作结果
- 编译器把变量保存在本地寄存器而不是内存中
- 编译器中生成的指令顺序,可以与源代码中的顺序不同
- 处理器采用乱序或并行的方式来执行指令
- 保存在处理器本地缓存中的值,对于其他处理器是不可见
在单线程中,只要程序的最终结果与在严格串行环境中执行的结果相同,那么上述所有操作都是允许的
在多线程中,JVM通过同步操作来找出这些协调操作将在何时发生
JMM规定了JVM必须遵循一组最小保证,这组保证规定了对变量的写入操作在何时将对其他线程可见
1、平台的内存模型
每个处理器都拥有自己的缓存,并且定期地与主内存进行协调,在不同的处理器架构中提供了不同级别的缓存一致性,即允许不同的处理器在任意时刻从同一个存储位置上看到不同的值。JVM通过在适当的位置上插入内存栅栏来屏蔽在JMM与底层平台内存模型之间的差异。Java程序不需要指定内存栅栏的位置,而只需通过正确地使用同步来找出何时将访问共享状态
2、重排序
各种使操作延迟或者看似乱序执行的不同原因,都可以归为重排序,内存级的重排序会使程序的行为变得不可预测
1 Thread one = new Thread(new Runnable() {
2 public void run() {
3 a = 1;
4 x = b;
5 }
6 });
上述代码也会有以下结果:
3、Java内存模式简介
Java内存模型是通过各种操作来定义的,包括变量的读/写操作,监视器的加锁和释放操作,以及线程的启动和合并操作
JMM为程序中所有的操作定义了一个偏序关系,称为Happens-Before,使在正确同步的程序中不存在数据竞争(缺乏Happens-Before关系,那么JVM可以对它们任意地重排序)
- 程序顺序规则。如果程序中操作A在操作B之前,那么在线程中A操作将在B操作之前执行
- 监视器锁规则。在监视器锁上的解锁操作必须在同一个监视器锁上的加锁操作之前执行。(显式锁和内置锁在加锁和解锁等操作上有着相同的内存语义)
- volatile变量规则。对volatile变量的写入操作必须在对该变量的读操作之前执行。(原子变量与volatile变量在读操作和写操作上有着相同的语义)
- 线程启动规则。在线程上对Thread.start的调用必须在该线程中执行任何操作之前执行
- 线程结束规则。线程中的任何操作都必须在其他线程检测到该线程已经结束之前执行,或者从Thread.join中成功返回,或者在调用Thread.isAlive时返回false
- 中断规则。当一个线程在另一个线程上调用interrupt时,必须在被中断线程检测到interrupt调用之前执行(通过抛出InterruptException,或者调用isInterrupted和interrupted)
- 终结器规则。对象的构造函数必须在启动该对象的终结器之前执行完成
- 传递性。如果操作A在操作B之前执行,并且操作B在操作C之前执行,那么操作A必须在操作C之前执行。
4、借助同步
”借助(Piggyback)“现有同步机制的可见性属性,对某个未被锁保护的变量的访问操作进行排序(不希望给对象加锁,而又想维护它的顺序)
Happens-Before排序包括:
- 将一个元素放入一个线程安全容器的操作将在另一个线程从该容器中获得这个元素的操作之前执行
- 在CountDownLatch上的倒数操作将在线程从闭锁上的await方法返回之前执行
- 释放Semaphore许可的操作将在从该Semaphore上获得一个许可之前执行
- Future表示的任务的所有操作将在从Future.get中返回之前执行
- 向Executor提交一个Runnable或Callable的操作将在任务开始执行之前执行
- 一个线程到达CyclicBarrier或Exchange的操作将在其他到达该栅栏或交换点的线程被释放之前执行。如果CyclicBarrier使用一个栅栏操作,那么到达栅栏的操作将在栅栏操作之前执行,而栅栏操作又会在线程从栅栏中释放之前执行
二、发布
造成不正确发布的真正原因:"发布一个共享对象"与"另一个线程访问该对象"之间缺少一种Happens-Before的关系
1、不安全的发布
除了不可变对象以外,使用被另一个线程初始化的对象通常都是不安全的,除非对象的发布操作是在使用该对象的线程开始使用之前执行
1 public class UnsafeLazyInitialization {
2 private static Object resource;
3
4 public static Object getInstance(){
5 if (resource == null){
6 resource = new Object(); //不安全的发布
7 }
8 return resource;
9 }
10 }
原因一:线程B看到了线程A发布了一半的对象
原因二:即使线程A初始化Resource实例之后再将resource设置为指向它,线程B仍可能看到对resource的写入操作将在对Resource各个域的写入操作之前发生。因为线程B看到的线程A中的操作顺序,可能与线程A执行这些操作时的顺序并不相同
2、安全发布
例:BlockingQueue的同步机制保证put在take后执行,A线程放入对象能保证B线程取出时是安全的
借助于类库中现在的同步容器、使用锁保护共享变量、或都使用共享的volatile类型变量,都可以保证对该变量的读取和写入是按照happens-before排序的
happens-before事实上可以比安全发布承诺更强的可见性与排序性
3、安全初始化模式
方式一:加锁保证可见性与排序性,存在性能问题
1 public class UnsafeLazyInitialization {
2 private static Object resource;
3
4 public synchronized static Object getInstance(){
5 if (resource == null){
6 resource = new Object(); //不安全的发布
7 }
8 return resource;
9 }
10 }
方式二:提前初始化,可能造成浪费资源
1 public class EagerInitialization {
2 private static Object resource = new Object();
3 public static Object getInstance(){
4 return resource;
5 }
6 }
方式三:延迟初始化,建议
1 public class ResourceFactory {
2 private static class ResourceHolder{
3 public static Object resource = new Object();
4 }
5 public static Object getInstance(){
6 return ResourceHolder.resource;
7 }
8 }
方式四:双重加锁机制,注意保证volatile类型,否则出现一致性问题
1 public class DoubleCheckedLocking {
2 private static volatile Object resource;
3 public static Object getInstance(){
4 if (resource == null){
5 synchronized (DoubleCheckedLocking.class){
6 if (resource == null){
7 resource = new Object();
8 }
9 }
10 }
11 return resource;
12 }
13 }
三、初始化过程中的安全性
- 如果能确保初始化过程的安全性,被正确构造的不可变对象在没有同步的情况下也能安全地在多个线程之间共享
- 如果不能确保初始化的安全性,一些本应为不可变对象的值将会发生改变
初始化安全性只能保证通过final域可达的值从构造过程完成时可见性。对于通过非final域可达的值,或者在构成过程完成后可能改变的值,必须采用同步来确保可见性
java并发编程实战:第十六章----Java内存模型的更多相关文章
- 《Java并发编程实战》第六章 任务运行 读书笔记
一. 在线程中运行任务 无限制创建线程的不足 .线程生命周期的开销很高 .资源消耗 .稳定性 二.Executor框架 Executor基于生产者-消费者模式.提交任务的操作相当于生产者.运行任务的线 ...
- java并发编程实战:第六章----任务执行
任务:通常是一些抽象的且离散的工作单元.大多数并发应用程序都是围绕"任务执行"来构造的,把程序的工作分给多个任务,可以简化程序的组织结构便于维护 一.在线程中执行任务 任务的独立性 ...
- 【java并发编程实战】第六章:线程池
1.线程池 众所周知创建大量线程时代价是非常大的: - 线程的生命周期开销非常大:创建需要时间,导致延迟处理请求,jvm需要分配空间. - 资源消耗:线程需要占用空间,如果线程数大于可用的处理器数量, ...
- 《Java并发编程实战》第三章 对象的共享 读书笔记
一.可见性 什么是可见性? Java线程安全须要防止某个线程正在使用对象状态而还有一个线程在同一时候改动该状态,并且须要确保当一个线程改动了对象的状态后,其它线程能够看到发生的状态变化. 后者就是可见 ...
- 《Java并发编程实战》第十一章 性能与可伸缩性 读书笔记
造成开销的操作包含: 1. 线程之间的协调(比如:锁.触发信号以及内存同步等) 2. 添加�的上下文切换 3. 线程的创建和销毁 4. 线程的调度 一.对性能的思考 1 性能与可伸缩性 执行速度涉及下 ...
- 《Java并发编程实战》第五章 同步容器类 读书笔记
一.同步容器类 1. 同步容器类的问题 线程容器类都是线程安全的.可是当在其上进行符合操作则须要而外加锁保护其安全性. 常见符合操作包括: . 迭代 . 跳转(依据指定顺序找到当前元素的下一个元素) ...
- 《Java并发编程实战》第七章 取消与关闭 读书笔记
Java没有提供不论什么机制来安全地(抢占式方法)终止线程,尽管Thread.stop和suspend等方法提供了这种机制,可是因为存在着一些严重的缺陷,因此应该避免使用. 但它提供了中断In ...
- 转:【Java并发编程】之十六:深入Java内存模型——happen-before规则及其对DCL的分析(含代码)
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17348313 happen-before规则介绍 Java语言中有一个"先行发生 ...
- 【Java并发编程】之十六:深入Java内存模型——happen-before规则及其对DCL的分析(含代码)
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17348313 happen-before规则介绍 Java语言中有一个"先行发生 ...
- 那些年读过的书《Java并发编程实战》十、再探究Java内存模型
1.什么是内存模型,为什么需要它? (1)内存模型的发展背景 近几年计算性能通过重排序实现了很大的提升,而且处理器也越来越朝着多核处理器发展以实现硬件的并行性.随着处理器的不断强大,编译器也在不断的改 ...
随机推荐
- springboot 有关拦截器遇到的问题
最近改造搜索服务,原来是用 ngx + lua 写的,虽然性能很高,带来的问题是可维护性不太方便,不是指lua语言方面,是因为团队就2个开发人员,另外一个开发人员的擅长语言是Java,于是准备将搜索服 ...
- Android中如何使用JUnit进行单元测试 eclipse
Android中如何使用JUnit进行单元测试 在我们日常开发android app的时候,需要不断地进行测试,所以使用JUnit测试框架显得格外重要,学会JUnit可以加快应用的开发周期. Andr ...
- ToastUtil
将Toast的调用封装成一个接口,写在一个公共的类当中 public class Util { private static Toast toast; public static void showT ...
- XSS获取cookie并利用
获取cookie利用代码cookie.asp <html> <title>xx</title> <body> <%testfile = Serve ...
- [转]Jsp 与 JavaBean
JavaBean 是一个遵循特定写法的 Java 类,它有以下特点: 1. Java 类具有一个无参的构造函数 2. 属性必须私有化. 3. 私有化的属性通过 public 类型的方法暴露给其它程序, ...
- Educational Codeforces Round 37-E.Connected Components?题解
一.题目 二.题目链接 http://codeforces.com/contest/920/problem/E 三.题意 给定一个$N$和$M$.$N$表示有$N$个点,$M$表示,在一个$N$个点组 ...
- 关于jQuery的$.proxy()应用.
今天在看<<锋利的jQuery>>时看到了proxy()的使用,感觉很模糊,就到处找资料. jQuery的源码也没看明白. 不过总算明白了proxy的用法了; <inpu ...
- 浏览器禁用Cookie
做JavaWeb的都知道Session的底层是使用Cookie来实现的,服务器端会在本地文件中保存session信息,并将sessionID发给客户端(浏览器),浏览器就会把这个sessionID(准 ...
- [saiku] schema文件分析
上一篇讲到了如何在管理台配置数据源 [ http://www.cnblogs.com/avivaye/p/4877767.html ] 这次来说明下shema文件里面是怎样配置Cube和角色权限的 通 ...
- 网页中显示pdf的方法
非常好的在网页中显示pdf的方法 今天有一需求,要在网页中显示pdf,于是立马开始搜索解决方案,无意中发现一个非常好的解决方法,详见http://blogs.adobe.com/pdfdevjunki ...