深入理解jvm 一 java内存模型与线程

摩尔定律:是由英特尔(Intel)创始人之一戈登·摩尔(Gordon Moore)提出来的。其内容为:当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍,性能也将提升一倍。换言之,每一美元所能买到的电脑性能,将每隔18-24个月翻一倍以上。这一定律揭示了信息技术进步的速度。

Amdahl定律:系统中对某一部件采用更快执行方式所能获得的系统性能改进程度,取决于这种执行方式被使用的频率,或所占总执行时间的比例。阿姆达尔定律实际上定义了采取增强(加速)某部分功能处理的措施后可获得的性能改进或执行时间的加速比。简单来说是通过更快的处理器来获得加速是由慢的系统组件所限制。


计算机的运算速度与他的存储和通信子系统速度差距太大!!!

如果不想让计算机长时间等待其他资源,就需要同时处理多任务


衡量一个服务器的性能好坏 TPS(每秒事务处理数)是重要指标,它代表着服务器一秒内平均能相应的请求总数

基于告诉缓存可以很好的解决处理器与内存之间的速度矛盾,但是在多处理器系统中,每个处理器都有自己的告诉缓存,而且他们又共享同一个主内存,当多个处理器的运算任务都设计同一块儿主内存区域就会带来数据不一致的问题,也就是缓存一致性的问题!


1、java内存模型

1.1主内存和工作内存

内存模型定义了虚拟机将变量存储到内存并且从内存取出的规则。此处的变量是:实例字段,静态字段和构成数组对象的元素

不包括局部变量和方法参数,因为他们是线程私有的。

java内存模型规定了所有的变量都存储在主内存,每条线程还有自己的工作内存。

!线程的工作内存保存了被该线程使用的变量的主内存副本拷贝。

!线程对变量的所有操作都在工作内存中进行。

!不同线程无法直接访问对方的工作内存中的变量。

!线程之间的变量值传递需要通过主内存来完成。


如果结合之前了解的虚拟机内存组成,主内存主要对应于java堆中对象实例数据部分,工作内存对应于虚拟机占中的部分区域


1.2内存间交互操作

主内存与工作内存之间的交互需要经历下面8种操作


1、lock锁定 作用于主内存的变量 把一个变量表示为一条线程独占的状态
2、unlock解锁 作用于主内存的变量 把一个处于锁定的变量释放,之后其他线程才能锁定它
3、read读取 作用于主内存的变量 把一个变量的值从主内存传输到线程的工作内存中
4、load载入 作用于工作内存的变量 将变量的值放入到工作内存的变量副本中
5、use使用 作用于工作内存的变量 把工作内存中的一个变量的值传递给执行引擎
6、assign赋值 作用于工作内存的变量 把一个从执行引擎获得值付给工作内存的变量
7、store存储 作用于工作内存的变量 将工作内存的变量值传递到主内存
8、write写入 作用于主内存的变量 将值写入主内存变量中
一些强制规定:

1.3volatile变量的特殊规则

volatile变量对所有的线程是立即可见的但是他并不是线程安全的。

当不满足下列两条时

1当在运算结果并不用来变量的当前值得时候或者能够保证只有一个单一线程修改变量的值得时候

2变量不需要参与其他状态变量共同参与的不变约束的时候

仍然需要增加锁来确保原子性

而且这个变量还会使得代码执行顺序与程序代码一致

1.4原子性、可见性、有序性

java内存模型是围绕着并发过程中如何处理原子性可见性和有序性这三个特城来建立的。

原子性:除了long和double不具备原子性协议之外,基本数据类型的访问读写都是具备原子性的

        在java代码中synchronize块儿之间的操作也具备原子性

可见性:可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。

        三个关键字可以实现可见性 volatile synchronized final

有序性:java语言提供了volatile和synchronize关键字来保证西安城之间操作的有序性。

volatile禁止了指令重排序的语义,synchronize使得一个变量在同一时刻只允许一条线程对其进行lock操作。

1.5先行发生原则

他判断数据之间是否存在竞争、线程是否安全的主要与依据。

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


2.Java线程

线程是比进程更轻量级的调度执行单位

线程的实现:每一个执行start方法且还未结束的thread类的实例就代表了一个线程,Thread类关键方法都是native的,一个native方法意味着这个方法没有使用或者无法使用平台无关的手段实现,也可能是为了执行效率。

实现线程的三种方式:使用内核线程实现,使用用户线程实现,使用用户线程加轻量级进程混合实现。

内核线程KLT:直接由操作系统内核支持的线程,由内核调度,负责将线程的任务映射到各个处理器上,每个内核线程都可以视为一个内核的分身。程序一般不会去执行内核线程,而是使用内核线程的高级接口,轻量级进程(LWP),其实每一种内核线程都是一一对应一个轻量级进程,并且都是一个独立的调度单元。即使有一个轻量级进程租塞了也不会影响整个进程工作,但是轻量级进程具有他的局限,系统调用代价相对较高,需要消耗一定的内核资源。


用户线程:完全建立在用户空间的线程库上,系统内核不能感知线程存在的实现。,用户线程的创建同步销毁都是在用户态中完成的,不需要内核的帮助,所以非常快速而且低消耗。但是缺点就是所有的创建 切换和调度的问题都需要自己解决,而且注入阻塞如何处理多处理器系统中如何将县城映射到其他的处理器上,这类问题会变得非常困难。因而用户线程实现的程序都一般比较复杂。


用户线程加轻量级进程混合:集合了上述两种特点,用户线程完全建立在用户空间中,并且操作系统提供支持轻量级进程作为用户线程的内核线程桥梁,大大降低阻塞风险。



3、java线程调度

协同是线程调度:线程的执行时间有线程本身控制,线程把自己的工作执行完了之后,主动通知系统切换到另外一个线程上。这样实现起来很简单,因为切换操作对线程自己是可知的,所以没有什么线程同步的问题。坏处也很明显就是线程执行时间不可控制没如果一个线程有问题出现了阻塞 其他线程也会跟着受影响。

抢占式线程调度:如果使用抢占式调度多线程系统,那么每个线程将由系统来分配执行时间,切换也不由线程决定。当一个线程出现问题,可以杀掉不至于导致系统崩溃。虽然java线程调度是系统自动完成的但是我们还是可以建议系统给一些线程多一点资源。java一共设置了10个级别的优先级来确保优先级越高的线程越容易被系统所选择。!!

4、状态转换

Java中的线程的生命周期大体可分为5种状态。
并且在任意一个时间点,一个线程只能有且只有一种状态
1、新建NEW:这种情况指的是,通过New关键字创建了Thread类(或其子类)的对象但是尚未启动。
2、运行RUNNABLE:这种情况指的是Thread类的对象调用了start()方法,这时的线程就等待时间片轮转到自己这,以便获得CPU
3、无限期等待waiting:这种状态线程不会被分配cpu时间,要等待被其他的线程显示的唤醒。以下方法会让一个线程陷入无限期的等待:wait()\Thread.join()\LockSupport.park()。
4、限期等待TimeWait:处于这种状态的线程不会被分配CPU执行时间,不过他们无需等待被其他线程唤醒,在一定的时间就会由系统自动唤醒,以下方法会让线程进入限期等待:Thread.sleep\设置Timeout的Object.wait\设置Timeout的Thread.join、LockSupport.parkNanos\LockSupport.parkUntil。

5、阻塞状态BLOCKED:一直在等待获取到一个排他锁,这个时间将在另一个线程放弃这个锁的时候发生

6、结束Terminated 种植线程的状态


猜你喜欢

转载自blog.csdn.net/qq_31615049/article/details/80298218