Java内存模型与线程(四)

时间记录:2020-2-3
上章节主要学习到了java内存模型的一些特性和long以及double的特殊规则,这次主要学习了java内存模型中的有序性支持的另一个规则【先行发生原则】,以及线程的实现和线程的状态,结合java线程的内容

先行发生原则

java中的特性包含了有序性,但是有序性的发生有volatile和synchronized,这个是之前学习到的部分,但是在java中如果完全靠这个来进行是会变的十分的繁琐而且很艰难,所以在java中定义了一些的原则来保证有序性的发生。这个先行发生叫做happens-before,其主要是为了判断数据是否存在竞争关系,线程是否安全的主要依据,就可以解决并发下的两个操作存在冲突的。其主要通过推导的方式来判断两个线程之间的操作是否存在冲突也就是关联,所以这样就能根据其的因果的关系来确定执行的顺序,从而避免了冲突带来的麻烦。当然如果操作之间没有什么关系的情况下,虚拟机就可以随意的改变其执行的顺序了,对结果和过程不会产生什么影响了。
在其中主要包括了一下的几条的规则

程序次序规则
在一个线程内按照代码的顺序来执行,写在前面的限制性,写在后面的后执行,这里的代码顺序不是指我看看到的顺序,而是机器看到的顺序也就是控制流顺序,像循环接口,分支等结构造成的代码顺序。

管程锁定规则
一个unlock操作必须而发生在同一个锁的lock后面,这里是指时间上的后面不是立即的后面。

volatile变量规则
对于一个volatile变量的写操作先先发生于这个变量的读操作后面,同样是时间上的后面

线程启动规则
Thread对象的start()方法先行于此线程的每一个动作

线程终止规则
和线程启动规则相对应的,线程所有的每一个动作都先发生于该线程的终止。可以通过Thread.join(),Thread.isAlive()来检测线程是否已经终止执行。

线程中断规则
对线程interrupt()方法的调用先行发生于被中断线程代码检测到的中断时间的发生,可以使用Thread.interrupted()方法检测是否发生中断。

对象终止规则
一个对象初始化完成先行于其的finalize()方法的开始,也就是对象析构在初始化后面。

传递性
指的是多个操作的发生一次存在关系,从而带有传递性A>B>C,所以A>C这种的

注意: 在其中有一个关于时间上先行发生和先行发生的区别
一个操作“时间上的先行发生”不代表这个操作会"先行发生",所以时间先后顺序与先行发生原则之间基本没有太大的关系,我们衡量并发发生安全问题的时候不要受到时间顺序的干扰,一切必须以先行发生原则为准,切记。

线程的实现

线程是比进程更轻量级的调度执行单位,有了线程就可以吧一个进程的资源分配和执行调度分开,各个线程可以共享进程资源又可以自己调度了。
线程的实现主要包括三种方式:使用内核线程实现使用用户线程实现使用用户线程和轻量级进程混合实现

使用内核线程实现
内核线程(KTL)就是直接由操作系统内核支持的线程,由内核来完成线程切换,内核通过调度器对线程来进行调度,并且将线程映射到各个处理器上。每一个内核线程可以理解为一个小的内核,这样操作系统就能够同时去做多件事情,这种内核也叫做多线程内核。
但是程序不会直接去利用这个内核线程的,而是使用一种叫做轻量级进程(LWP),每一个轻量级进程都由一个内核线程支持,一对一的关系。
P=n=>LWP=1=>KLT==>Thread Scheduler=n=>CPU
但是轻量级进程存在一个局限性,由于是基于内核线程实现的,所以各种线程操作都需要进行系统调用,而系统调用的代价是比较大的,需要在用户态和内核态来回的切换,而且轻量级进程是由内核支持的需要占用一定的内核,所以一个系统支持的轻量级进程的数量是有限的。
注意: 这里谁的内核态和用户态来回切换,也就是大家常说的多线程不一定比单线程快,redis就是案例。

使用用户线程实现
从广义上来说一个线程只要不是内核线程都可以认为是用户线程,从狭义上来说只有建立在用户空间的线程,不被内核感知的线程被称为用户线程。
用户的线程的操作例如创建销毁都会在用户态,不需要内核的支持,这样就不存在用户态和内核态的来回切换的情况了,这样的操作可以是非常快速且低耗的,部分高性能数据库的多线程就是由用户线程实现的。但是在线程的调度处理,如何将线映射到处理器上面比较繁琐。java曾经都使用过用户线程但是后来都放弃了它。
UT=n=>P==>CPU

使用用户线程和轻量级进程混合实现
有了上面的两种实现方式就取两种的优点实现,使用用户线程创建切换,使用内核提供的线程调度将其映射到处理器上,将内核线程和用户线程结合起来使用。

java线程的实现
java在1.2前是基于称为“绿色线程”的用户线程来实现的,在1.2就替换为基于操作系统原生线程模型来实现,所以操作系统支持什么样的线程模型,就决定了java虚拟机是怎样的映射了。我们常用的windows和linux都是一对一的线程模型,也就是基于内核线程实现的。

java线程调度

线程调度是指系统为线程分配处理器使用权的过程,主要为协作式线程调度抢占式线程调度

协同式线程调度
此方式是指线程的执行时间由线程本身来控制,线程把自己的工作执行完了之后,要主动通知系统切换到另外一个线程上,这种实现最为简单。但是如果当前这个线程出现问题了就会导致整个的程序阻塞在这里,会造成十分的不稳定,造成整个系统的崩溃。

抢占式线程调度
每个线程的执行时间由系统来分配,线程的切换由线程本身来决定(Thread.yield()让出执行时间,也就是让出时间片),目前java使用的线程调度方式就是抢占调度的。
注意: 在java中与一个线程优先级的参数,但是此参数不靠谱的,线程是通过映射到系统原生线程上实现的,所以线程的调度最终还是要取决于下同的调度,此项在think in java 也提及了不建议使用此项。

线程状态转换

java定义了线程的5中状态,而且一个线程在一个时刻只能有着一种状态

新建[New]
创建后尚未启动

运行[Runnable]
包括了系统线程状态中的RunningReady,也就是此线程可能是在执行的也可能是等待cpu分配执行时间的。

等待[Waiting]
等待状态分为期限等待和无期限等待两种,且处于这联众状态的不会被分配cpu执行时间,期限等待会自我唤醒而无期限等待需要其他来进行唤醒。

阻塞[Blocked]
阻塞状态是线程正在获取一个排它锁,这个锁在另一个线程放弃的时候,此线程获取锁才会接触此状态。常见就是同步操作,会导致这个。注意此状态是会占用cpu时间的

结束[Terminated]
线程执行结束并且终止

时间记录:2020-2-3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值