DepthJVM-Java内存模型与线程

1、内存交互
主内存:线程共享的JVM部分内存
工作内存:线程独有,线程对变量的所有操作均在工作内存中进行,线程间变量值传递需要通过主内存来完成
交互操作:
1.lock(锁定,作用于主内存变量,把一个变量标识为一个线程独占状态)
2.unlcok(解锁,作用于主内存变量,释放处于锁定状态的变量)
3.read(读取,作用于主内存变量,把一个变量值从主内存传输到线程的工作内存,以便随后的load操作使用)
4.load(载入,作用于工作内存变量,把read操作从主内存得到的变量值放入工作内存的变量副本)
5.use(使用,作用于工作内存变量,把工作内存中一个变量值传递给执行引擎,虚拟机遇到需要使用变量值的字节码指令时执行该操作)
6.assign(赋值,作用于工作内存变量,把一个从执行引擎接收到的值赋给工作内存变量,虚拟机遇到一个给变量赋值的字节码执行时执行该操作)
7.store(存储,作用于工作内存变量,把工作内存中一个变量值传送到主内存,以便随后的write操作使用)
8.write(写入,作用于主内存变量,把store操作从工作内存得到的变量值写入主内存变量中)
操作规则:
1.不允许read和load、store和write操作之一单独出现,必须成对,不必连续执行
2.不允许一个线程丢弃它最近的assign操作,即变量在工作内存中改变了之后必须同步到主内存
3.不允许一个线程无原因地(没有发生过任何assign)把数据从工作内存同步到主内存
4.一个变量必须在主内存“诞生”,工作内存不允许直接使用一个未被初始化(load或assign)的变量,即对一个变量执行use、store之前必须先执行过assign和load
5.一个变量在同一时刻只允许一条线程对其进行lock,但一个线程可以多次lock,只有执行相同数量的unlock之后变量才会被解锁
6.一个线程执行lock会清空工作内存中此变量值,必须重新load或assign之后才能给执行引擎使用
7.一个变量未经lock不允许执行unlock,也不允许unlock一个被其他线程锁定的变量
8.一个变量unlock之前,必须把变量同步回主内存(store、write)
3、特殊规则
1.volatile:可见性、禁止指令重排序,不保证原子性,不符合如下两条规则仍要使用synchronized/concurrent(1.运算结果不依赖当前值,或者,单一线程修改变量值 2.变量不需要与其他状态变量共同参与不变约束),操作规则(1.read->load->use连续出现 2.assign->store-write连续出现 3.假设变量V的use/assign先于变量W的use/assign,则变量V的read/write先于变量W的read/write)
2.long/double:JVM不保证原子性,可以分为两次32位操作,但目前商用JVM都把64位数据读写操作作为原子操作对待
4、原子性、可见性、有序性
1.原子性: read、load、assign、use、store、write能保证变量原子性;lock、unlock能保证更大范围的原子性,lock/unlcok未直接开放给用户,但可以通过更高层次的字节码命令monitorenter/monitorexit隐式调用,体现到java代码里就是synchronized
2.可见性:当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。Java内存模型是通过将修改后变量值同步回主内存,读取前从主内存刷新到工作内存的方式来保证可见性。volatile变量保证立即同步回主内存,且读取前立即从主内存刷新,普通变量则无法保证。synchronized和final也能保证可见性,同步块的可见性是由"对一个变量unlock之前,必须先同步回主内存"实现的;final字段一旦初始化完成,且构造器没有把this引用传递出去(this引用逃逸,其他线程可能访问到"初始化一半"的对象),其他线程就能看见final字段值
3.有序性:如果在本线程内观察,所有操作都是有序的;在一个线程观察另一个线程,所有操作都是无序的。前半句是"线程内表现为串行",后半句是"指令重排序"现象和"工作内存和主内存同步延迟"现象。synchronized和volatile保证线程之间操作的有序性。
5、happens-before(先行发生原则)
先行发生于时间上先发生没有关系,无须任何同步手段即可保证以下规则:
1.程序次序规则:在一个线程内依据程序代码顺序,书写在前面的操作 先于后面的操作(准确说应该是依据控制流顺序,因为要考虑分支、循环等结构)
2.管程锁定规则:一个unlock操作 先于后面对同一个锁的lock操作,后面是从时间上
3.volatile变量规则:对一个volatile变量的写 先于对改变量后面的读
4.线程终止规则:线程中所有操作都 先于对此线程的终止检测,可以通过Thread.join结束、Thread.isAlive返回值检测线程是否终止
5.线程中断规则:线程interrupt调用 先于该线程代码检测到中断时间的发生,可以通过Thread.interrupted检测是否有中断发生
6.对象终结规则:对象初始化完成(构造函数执行) 先于它的finalize方法开始
7.传递性:操作A先于操作B,操作B先于操作C,则A先于C
6、线程实现
内核线程实现:内核线程就是直接由操作系统内核支持的线程,切换也由内核来完成,每个内核线程可以看做内核的一个分身。程序一般不会直接使用内核线程,通常使用内核线程的一种高级接口--轻量级进程(通常意义上的线程),每个轻量级进程由一个内核线程支持,这种轻量级进程与内核线程1:1的关系称为 一对一线程模型。轻量级进程局限性:1.基于内核实现,线程各种操作都要进行系统调用,会在用户态和内核态之间来回切换,代价较高 2.需要消耗一定的内核资源(如栈空间),一个系统支持的轻量级进程也是有限的
用户线程实现:广义讲非内核线程都是用户线程(包括轻量级进程),狭义将用户线程是完全建立在用户空间的线程库上,内核线程无法感知。用户线程建立、同步、销毁和调度都在用户态完成,不需要内核帮助。这种进程与用户线程1:N的关系称为 一对多线程模型。局限在于优于没有内核支持,诸如"阻塞如何处理"、"多处理器系统中如何将线程映射到其他处理器上"等问题解决困难,现在用户线程基本不再使用
用户线程+轻量级进程混合实现:轻量级进程作为用户线程和内核线程的桥梁,用户线程操作依然在用户空间,支持大规模并发;内核线程提供调度功能和处理器映射,用户线程的系统调用通过轻量级进程来完成,大大降低了整个进程被完全阻塞的风险。用户线程和轻量级进程数量不固定,N:M的关系,称为 多对多线程模型
java线程实现:JVM线程映射在很大程度上由操作系统支持的线程模型决定,Windows和Linux都提供一对一线程模型,一个用户线程映射到一条轻量级进程之中;Solaris即支持一对一(-XX:+UseBoundThreads_)也支持多对多(-XX:+UseLWPSynchronization,默认)
7、线程调度
协同式线程调度:线程执行时间由自身控制,执行完成之后通知系统切换到另一个线程。优势是实现简单,没有线程同步问题;劣势是线程执行时间不可控,导致阻塞
抢占式线程调度:线程执行时间由系统控制。可以通过线程优先级(10个)"建议"系统分配,但不靠谱,线程调度最终还是取决于操作系统
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值