java内存模型与线程

一、硬件的效率与缓存一致性
1.解决计算机硬件处理器和内存交互缓慢的问题,在计算机系统中加入一层高速缓存作为中介。

2.每一个处理器都有一个缓存,但是却共享一个内存,会出现缓存不一致问题,处理器在访问缓存的时候要遵循一些协议。

3.处理器使用乱序执行优化使得处理器内部的运算单元尽量被充分利用。

这里写图片描述

二、java内存模型
1.主内存和工作内存
(1)java内存模型的定义:在虚拟机中将变量存储到内存中和从内存中取出变量这样的底层细节称为java 内存模型。此处变量是指实例字段、静态字段和构成数组对象的元素,不包括局部变量和方法参数。

(2)工作内存:每条线程都有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行。不同线程之间无法访问对方的工作线程,线程之间的变量值传递要通过主内存。

这里写图片描述

2.内存间交互操作
(1)java 内存模型中8中原子性、不可再分的操作实现内存之间的交互协议:lock(主内存的变量,线程锁定状态);unlock(主内存的变量,释放线程锁定状态);read(主内存的变量,从主内存中读取);load(工作内存的变量,拷贝副本放入);use(工作内存的变量,遇到需要使用的字节码指令执行);assign(工作内存的变量,遇到给变量赋值的字节码指令执行);store(工作内存的变量,获取存储工作内存的变量);write(主内存的变量,写入工作内存获取的变量值到主内存)

(2)8中基本操作必须遵循的规则:不允许read、load和store、write单独出现;变量在工作内存中改变了之后必须要同步会主内存;不允许无原因的把变量从工作内存中同步到主内存;工作内存中不可使用未被初始化的变量;一个变量可以被同一线程进行多次lock,并且unlock要和lock数相等;一个变量执行lock操作,会清空工作内存中这个变量的值;没有被lock的变量不允许使用unlock操作;执行unlock之前必须把变量同步会主内存中。

3.validate变量的特殊规则
(1)关键字validate是java 虚拟机提供的最轻量级的同步机制。它具备两种特性,一是保证所有线程的可见性;二是禁止指令重排序优化,指令重排序无法越过内存屏障。validate变量的读操作和普通的变量几乎没什么差别,写操作会慢一点,因为它要在本地代码中插入许多内存屏障来保证处理器不发生乱序执行。

(2)validate变量在以下的应用场景中无法保证其原子性,要使用互斥锁:运算结果依赖变量的当前值i++;变量需要与其他的状态变量共同参与不变约束。

(3)java 内存模型中对validate变量定义的特殊规则:在工作内存中,每次使用变量前,都必须从主内存刷新最新的值,用于保证能看见其他的线程对变量所做的修改后的值;在工作内存中,每次修改变量后都必须立即同步回主内存中用于保证其他的线程能看见对变量所做的修改后的值;validate修饰的变量不会被指令重排序优化,保证代码的执行顺序与程序的顺序相同。

4.原子性、可见性和有序性
(1)原子性:原子性是指不可再分的最小操作指令,即单条机器指令,原子性操作任意时刻只能有一个线程,因此是线程安全的。Java内存模型中通过read、load、assign、use、store和write这6个操作保证变量的原子性操作。所以java中基本数据类型的访问读写是原子性操作。对于大范围的原子性保证需要通过lock和unlock操作以及synchronized同步块来保证。

(2)可见性:当一个线程修改了共享变量的值,其他线程能够立即得知这个修改,例如validate关键字,synchronized(对一个变量unlock操作之前,必须先把变量同步回主内存中)、final(被final修饰的字段一旦在构造器中初始化完成,并且构造器没有把“this”引用传递出去,那么其他线程就能看见final字段的值)。

(3)有序性:java语言提供volidate和synchronized两个关键字来保证线程之间的操作的有序性。validate,包含了禁止指令重排序的语义;synchronized,一个变量在同一时刻只允许一条线程对其进行lock操作。

5.先行发生原则
(1)先行发生原则遵循的规则:程序次序规则,在一个线程内,按照程序代码的顺序执行;管程锁定规则,一个unlock操作发生于后面对同一个锁的lock操作之前;validate变量规则,对一个validate变量的写操作先行发生于后面对它的读操作之前;线程启动规则,Thread的start方法最先发生;线程终止规则,线程中所有操作都先行发生于对此线程的终止检测;线程中断规则,对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断线程事件的发生;对象终结规则,一个对象的初始化先行与它的finalize();传递性,A先行B,B先行C,那么A先行C。

(2)时间先后顺序与先行发生原则之间基本没有太大关系,所以衡量并发安全问题的时候不要受到时间顺序的干扰,一切必须以先行发生原则为准。

三、java与线程
1.线程的实现
这里写图片描述

Kernal thread:KLT,内核线程,运行在内核态,是直接有操作系统内核支持的线程,有操作系统内核完成内核线程切换,内核操作线程调度器Threadscheduler对内核线程进行调度,负责将内核线程任务映射到各个处理器上。

Light weight process: LWP,轻量级用户进程,是编程中传统意义上的线程,每个轻量级进程都由一个内核线程支持。

User thread:UT,用户线程,运行在用户态,完全由用户空间线程库实现,内核线程无法感知到用户线程的实现,用户线程的创建、同步、调度和销毁完全在用户态中完成,不需要内核态的支持。

JDK的线程是基于操作系统原生线程模型来实现的,因此JDK版本中线程模型取决于java虚拟机线程与操作系统线程的映射,在不同平台上是不同的。

2.java线程调度
(1).协同式:线程的执行时间由线程本身来控制,线程任务执行完成之后主动通知系统切换到另一个线程去执行。
优点:实现简单,线程切换操作对线程本身是可知的,不存在线程同步问题。
缺点:线程执行时间不可控制,如果线程长时间执行不让出CPU执行时间可能导致系统崩溃。

(2).抢占式:每个线程的执行时间有操作系统来分配,操作系统给每个线程分配执行的时间片,抢到时间片的线程执行,时间片用完之后重新抢占执行时间,线程的切换不由线程本身来决定。
优点:线程执行时间可控制,不会因为一个线程阻塞问题导致系统崩溃。

*当前JDK的多线程是抢占式的多线程系统,但是可以通过设置线程优先级和改变线程的执行时间分配概率。
注意:由于JDK的线程优先级和操作系统的线程优先级不是一一对应的,因此建议只使用1(最低优先级)、5(正常优先级)和10(最高优先级)这三个优先级。
另外,线程优先级只是操作系统给线程分配执行时间的概率大小,不是绝对的。*

3.Java中线程的状态即调度关系图
这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值