(转)《深入理解java虚拟机》学习笔记9——并发编程(一)
随着多核CPU的高速发展,为了充分利用硬件的计算资源,操作系统的并发多任务功能正变得越来越重要,但是CPU在进行计算时,还需要从内存读取输出,并将计算结果存放到内存中,然而由于CPU的运算速度比内存高几个数量级,CPU内的寄存器数量和容量有限,为了不让CPU长时间处于等待内存的空闲状态,在CPU和内存之间引入了速度接近CPU的高速缓存Cache作为CPU和内存之间的缓冲。计算机硬件并发的原理如下:
Java虚拟机对并发的支持类似于计算机硬件,java虚拟机的并发支持是通过java虚拟机的内存模型来实现的。Java虚拟机的内存模型分为主内存和工作内存,程序中所有的变量都存储在主内存中,每个线程有自己的私有工作内存,工作内存中保存了被该线程使用到的变量的主内存拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量,不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递需要通过主内存来完成。Java虚拟机并发原理如下:
Java虚拟机内存模型中定义了8种关于主内存和工作内存的交互协议操作:
(1).lock锁定:作用于主内存的变量,把一个变量标识为一条线程独占状态。
(2).unlock解锁:作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量可以被其他线程锁定。
(3).read读取:作用于主内的变量,把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
(4).load加载:作用于工作内存的变量,把read读取操作从主内存中得到的变量值放入工作内存的变量拷贝中。
(5).use使用:作用于工作内存的变量,把工作内存中一个变量的值传递给java虚拟机执行引擎,每当虚拟机遇到一个需要使用到变量值的字节码指令时将会执行该操作。
(6).assign赋值:作用于工作内存变量,把一个从执行引擎接收到的变量的值赋值给工作变量,每当虚拟机遇到一个给变量赋值的字节码时将会执行该操作。
(7).store存储:作用于工作内存的变量,把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。
(8).write写入:作用于主内存的变量,把store操作从工作内存中得到的变量值放入主内存的变量中。
Java内存模型对上述8种操作有如下的约束:
(1).把一个变量从主内存复制到工作内存中必须顺序执行read读入操作和load载入操作。
把一个变量从工作内存同步回主内存中必须顺序执行store存储操作和write写入操作。
read和load操作之间、store和write操作之间可以插入其他指令,但是read和load操作、store和write操作必须要按顺序执行,即不允许read和load、store和write操作之一单独出现。
(2).不允许一个线程丢弃它的最近的assign赋值操作,即工作内存变量值改变之后必须同步回主内存。只有发生过assign赋值操作的变量才需要从工作内存同步回主内存。
(3).一个新变量只能在主内存中产生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量,即一个变量在进行use和store操作之前,必须先执行过assgin和load操作。
(4).一个变量在同一时刻只允许一条线程对其进行lock锁定操作,但是lock锁定可以被一条线程重复执行多次,多次执行lock之后,只有执行相同次数的unlock操作变量才会被解锁。
(5).如果对一个变量执行lock锁定操作,将会清空工作内存中该变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。
(6).如果一个变量事先没有被lock锁定,则不允许对这个变量进行unlock解锁操作,也不允许对一个被别的线程锁定的变量进行unlock解锁。
(7).一个变量进行unlock解锁操作之前,必须先把此变量同步回主内存中(执行store和write操作)。
Java中的关键字volatile是java虚拟机提供的最轻量级的线程同步机制,当一个变量被声明为volatile之后,该变量将具备以下两种特性:
(1).volatile保证变量对所有线程的可见性,即任何一个线程修改了该变量的值之后,新值对于所有其他线程都是可以立即得知的。
而普通变量需要先将工作内存中的变量同步回主内存,其他线程都需要从主内存重新读取变量的值才能使用最新修改后的值。
volatile变量也可以在各个工作内存中存在不一致的情况,但由于每次使用之前都需要先刷新(工作内存变量重新执行初始化),执行引擎看不到变量不一致的情况,因此可以任务volatile变量不存在不一致的情况。
但是java中的运算并非全部都是原子操作,因此volatile变量的运行在并发下一样是线程不安全的。
由于volatile变量只能保证可见性,只有在符合如下两条规则情况才是线程安全的。
a.运算结果不依赖变量的当前值,或者能够确保只有单一线程修改变量的值。
b.变量不需要与其他其他变量共同参与不变约束。
不符合上述两条规则情况下,仍然需要通过synchronized同步关键字或者加锁机制来保证线程安全。
(2).volatile禁止指令重排序优化。
普通变量仅能保证在方法执行过程中所有依赖赋值结果的地方都能获取正确的结果,而无法保证变量赋值操作顺序与程序代码执行顺序一致。
volatile禁止指令重排序,因此volatile变量的约束如下:
a.volatile变量的操作必须按read->load->use顺序,即每次在工作内存中使用变量前必须先从主内存中刷新最新的值,以保证能看到其他线程对变量的最新修改。
b. volatile变量的操作必须按assign->store->write顺序,即每次在工作内存为变量赋值之后必须将变量的值同步回主内存,以保证让其他线程能看到变量的最新修改。
c.若线程对volatile变量A的assign或者use操作先于对volatile变量B的assign或者use操作,则线程对volatile变量A的read/load或者store/write操作也必定先于对volatile变量B的read/load或者store/write操作。
(转)《深入理解java虚拟机》学习笔记9——并发编程(一)的更多相关文章
- 深入理解java虚拟机学习笔记(一)JVM内存模型
上周末搬家后,家里的宽带一直没弄好,跟电信客服反映了N遍了终于约了个师傅明天早上来迁移宽带,可以结束一个多星期没网的痛苦日子了.这段时间也是各种忙,都一个星期没更新博客了,再不写之前那种状态和激情都要 ...
- 深入理解java虚拟机学习笔记(二)垃圾回收策略
上篇文章介绍了JVM内存模型的相关知识,其实还有些内容可以更深入的介绍下,比如运行时常量池的动态插入,直接内存等,后期抽空再完善下上篇博客,今天来介绍下JVM中的一些垃圾回收策略. 一. ...
- 深入理解Java虚拟机学习笔记(一)-----Java内存区域
一 概述 对于 Java 程序员来说,在虚拟机自动内存管理机制下,不再需要像C/C++程序开发程序员这样为内一个 new 操作去写对应的 delete/free 操作,不容易出现内存泄漏和内存溢出问题 ...
- 深入理解Java虚拟机 - 学习笔记 1
Java内存区域 程序计数器 (Program Counter Register) 是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里,字节码解释器工作时就是通过 ...
- 深入理解java虚拟机学习笔记(二)
第三章 垃圾收集器与内存分配策略 概述 程序计数器.虚拟机栈.本地方法栈3个区随线程而生,随线程而灭.因此大体上可认为这几个区域的内存分配和回收都具备确定性.在方法/线程结束时,内存自然就跟着回收 ...
- 深入理解java虚拟机学习笔记(一)
第二章 Java内存区域与内存溢出异常 运行时数据区域 程序计数器(Program Counter Register) 程序计数器:当前线程所执行的字节码行号指示器.各条线程之间计数器互不影响,独立存 ...
- 深入理解Java虚拟机学习笔记(三)-----类文件结构/虚拟机类加载机制
第6章 类文件结构 1. 无关性 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码(即扩展名为 .class 的文件) 是构成平台无关性的基石. 字节码(即扩展名为 .class 的文 ...
- 深入理解Java虚拟机学习笔记(二)-----垃圾收集器与内存分配策略
写在前面 本节常见面试题: 如何判断对象是否死亡(两种方法). 简单的介绍一下强引用.软引用.弱引用.虚引用(虚引用与软引用和弱引用的区别.使用软引用能带来的好处). 如何判断一个常量是废弃常量 如何 ...
- 《深入理解Java虚拟机》笔记04 -- 并发锁
Java虚拟机在操作系统层面会先尽一切可能在虚拟机层面上解决竞争关系,尽可能避免真实的竞争发生.同时,在竞争不激烈的场合,也会试图消除不必要的竞争.实现这些手段的方法包括:偏向锁.轻量级锁.自旋锁.锁 ...
- 类加载机制(深入理解JAVA虚拟机学习笔记)
1.类加载机制的定义 将class文件加载到内存,然后对class文件中的数据进行校验.解析和初始化,转换成可以被虚拟机直接使用的JAVA类型,这就是虚拟机的类加载机制.(在JAVA中,类的加载.连接 ...
随机推荐
- linux 安装GCC
研究生阶段已经开始了一段时间了,选了LINUX深入分析,之前没怎么接触过,感觉还是有点难度的.不,好像是很难. 从学校借了一台电脑,安装了UBUNTU12.04的系统,可是不知道怎么地,这个系统里,没 ...
- Git CMD - diff: Show changes between commits, commit and working tree, etc
命令格式 git diff [options] [<commit>] [--] [<path>…] git diff [options] --cached [<comm ...
- MTKLogger日志记录打开
MTKLogger在 拨号界面用*#446633#(不同手机可能设置不一样) 向左滑动到Log and Debugging下面有个MTKLogger,点进去 点击右上角进入设置,打开MobileLog ...
- iBeacon
iBeacon[1] 是苹果公司2013年9月发布的移动设备用OS(iOS7)上配备的新功能.其工作方式是,配备有 低功耗蓝牙(BLE)通信功能的设备使用BLE技术向周围发送自己特有的ID,接收到该I ...
- sqlserver关于事务
事务的四个特性: 原子性,一致性,持久性,隔离性 原子性: 原子性:表示事务执行是作为原子,不可分割,整个语句要么执行,要么不执行sqlserver中每一个单独的语句可以看做是包含在事务中每一句本身具 ...
- Linux系统下给VMWare安装Tools
1.进入Linux系统. 2.在VMware的窗口菜单中选VM >> install VMware Tools,虚拟机自动将VMware-tools装入虚拟光驱中. 3.在虚拟光驱里找出V ...
- FreeMarker语法
向原作者致敬,原文地址http://www.cnblogs.com/linjiqin/p/3388298.html FreeMarker的插值有如下两种类型:1,通用插值${expr};2,数字格式化 ...
- out ref区别
1.使用ref型参数时,传入的参数必须先被初始化.对out而言,必须在方法中对其完成初始化. 2.out适合用在需要retrun多个返回值的地方,而ref则用在需要被调用的方法修改调用者的引用的时候. ...
- (转)实战Memcached缓存系统(6)Memcached CAS的多线程程序实例
1. 源程序 package com.sinosuperman.memcached; import java.io.IOException; import java.net.InetSocketAdd ...
- uva247 - Calling Circles(传递闭包+DFS)
题意:两人相互打电话(直接或间接),则在一个电话圈.即a给b打电话,b给c打电话,则a给c间接打电话. 注意:1.注意标记.2.注意输出格式. #include<iostream> #in ...