Java内存模型和线程

2018-03-22  本文已影响0人  icelovesummer

【个人笔记】

1.JMM

Java内存模型:虚拟机定义的一种方式来屏蔽各种意见和操作系统的内存访问差异,以实现让Java程序在各种平台都能达到一直的内存访问效果。

1.1 主内存和工作内存

注意:这里的主内存和工作内存的概念和堆、栈、方法区 不是同一个层次的划分。

1.2 内存间的交互操作

每一个操作都是原子的、不可分的。对于long和double的load、write、read和store在某些平台例外。

一些规则:

1.3 volatile

volatile是JVM中最轻量级的同步机制,有2种特性:

  1. 保证变量的可见性:对volatile变量的写操作可以立刻反应到所有线程中。
  2. 禁止指令重排序。插入内存屏障保证后面的指令不会重排序到屏障之前。

volatile语义并不能保证变量的原子性。对任意单个volatile变量的读/写具有原子性,但类似于i++、i–这种复合操作不具有原子性,因为自增运算包括读取i的值、i值增加1、重新赋值3步操作,并不具备原子性。

1.4 long和double的非原子协定

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

原子性:JMM保证的原子性变量操作包括read、load、assign、use、store、write,而long、double非原子协定导致的非原子性操作基本可以忽略。如果需要对更大范围的代码实行原子性操作,则需要JMM提供的lock、unlock、synchronized等来保证。

可见性:前面分析volatile语义时已经提到,可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。JMM在变量修改后将新值同步回主内存,依赖主内存作为媒介,在变量被线程读取前从内存刷新变量新值,保证变量的可见性。普通变量和volatile变量都是如此,只不过volatile的特殊规则保证了这种可见性是立即得知的,而普通变量并不具备这种严格的可见性。除了volatile外,synchronized和final也能保证可见性。

有序性:JMM的有序性表现为:如果在本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。前半句指“线程内表现为串行的语义”(as-if-serial),后半句值“指令重排序”和普通变量的”工作内存与主内存同步延迟“的现象。

1.6 先行发生原则

Java规定的一种规则,用于判断是否线程安全。满足的这些条件的无须任何线程同步手段。

例如:

//假设线程A在时间上先调用setValue(1),然后线程B调用getValue()方法,那么线程B收到的返回值一定是1吗?
private int value = 0;

public void setValue(int value) {
    this.value = value;
}

public int getValue() {
    return this.value;
}
//按照happens-before原则,两个操作不在同一个线程、没有通道锁同步、线程的相关启动、终止和中断以及对象终结和传递性等规则都与此处没有关系,因此这两个操作是不符合happens-before原则的,这里的并发操作是不安全的,返回值并不一定是1。

2. 线程

2.1 线程的实现(不特指Java线程)

使用内核线程实现
使用用户线程实现
使用用户线程加轻量级进程实现

内核线程:KLT,直接由操作系统内核支持的线程,由内核完成线程切换,内核通过操纵调度器(Scheduler)对线程进行调度,并负责将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身,这样操作系统就有能力同时处理多件事情,支持多线程的内核叫做多线程内核。
  程序一般不会去直接使用内核线程,而是去使用内核线程的一种高级接口——轻量级进程(LWP),即通常意义上的线程。由于每个轻量级进程都由一个内核线程支持,因此只有先支持内核线程,才能有轻量级进程。*轻量级进程与内核线程之间1:1关系称为一对一的线程模型。

轻量级进程和内核线程的1:1关系
优点:内核线程保证了每个轻量级进程都成为一个独立的调度单元,即时有一个轻量级进程在系统调用中阻塞了,也不会影响整个进程的继续工作。
缺点:基于内核线程实现,因此各线程操作等需要系统调用,系统调用代价高,需要在用户态和内核态来回切换,其次,每个轻量级进程都需要一个内核线程的支持,因此轻量级进程要消耗一定的内核资源,如内核线程的栈空间,因此一个系统支持轻量级进程的数量是有限的。

用户线程:用户线程指完全建立在用户空间的线程库上。这种线程不需要切换内核态,效率非常高且低消耗,也可以支持规模更大的线程数量,部分高性能数据库中的多线程就是由用户线程实现的。

进程和用户线程1:N关系
优缺点:用户线程优势在于不需要系统内核支援,劣势也在于没有系统内核的支援,所有的线程操作都是需要用户程序自己处理。阻塞处理等问题的解决十分困难,甚至不可能完成。所以使用用户线程会非常复杂。

用户线程加轻量级进程混合:这种混合下,既存在用户线程,也存在轻量级进程。用户线程还是完全建立在用户空间中,因此用户线程的创建,切换,析构等操作依然廉价,并且可以支持大规模的用户线程并发。而轻量级进程则作为用户线程和内核线程之间的桥梁,这样可以使用内核提供的线程调度功能及处理器映射,并且用户线程的系统调用要通过轻量级线程来完成,大大降低整个进程被完全阻塞的风险。

用户线程和轻量级进程N:M关系

2.2 线程调度

协同式:线程执行时间由线程本身控制,自己执行完了以后通知系统进行切换。线程执行时间不可控,容易GG。
抢占式:线程执行时间由系统来分配。Java是抢占式。

上一篇 下一篇

猜你喜欢

热点阅读