Java内存模型与线程

时间:2022-12-27 00:10:57

前言

本文由PPT整理而来。

Page1

目录

  • 进程与线程
  • Java内存模型
  • Java线程

Page2

进程

  • 一般一个应用程序对应一个进程
  • 进程有自己的资源和内存地址空间
  • 各个进程之间互不干扰
  • 进程会保存程序的运行状态,支持切换及恢复
  • 进程使操作系统看起来同一时刻有多个任务在执行

Page3

线程

  • 一个进程可以有多个线程
  • 多个线程共享同一进程的资源和内存空间
  • 线程创建、调度、切换及线程间通信的开销小于进程
  • UI线程编程模型
  • 多线程并发执行可能会引发问题

Page4

小结

  • 进程是操作系统进行资源分配的基本单位
  • 线程是操作系统进行调度的基本单位
  • 进程让操作系统的并发成为可能
  • 线程让应用程序的并发成为可能

Page5

思考

  • 思考1:为什么要发明线程?何不用多进程来取代多线程?
  • 思考2:多线程性能是否一定优于单线程?
  • 思考3:并发与并行有什么区别?

Page6

Java内存模型

  • 概念理解
  • 缓存一致性
  • 内存间交互协议
  • volatile语义
  • happens-before原则
  • 原子性
  • 可见性
  • 有序性

Page7

物理机层面的并发

Java内存模型与线程

Page8

应用程序层面的并发

Java内存模型与线程

Page9

概念理解

  • 内存模型可以理解为在特定的操作协议下,对特定的内存或高速缓存进行读写访问的过程抽象。不同的物理机可能有不同的内存模型,JVM也有自己的内存模型,基本原理是类似的。
  • JVM是跨平台的,为了实现跨平台,Java虚拟机规范视图定义一种JMM(Java Memory Model)来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台上都能达到一致的内存访问效果。对比C/C++等非跨平台语言,需针对不同平台写程序,同时体现了JVM这个抽象层的好处。
  • Java内存模型规定所有的变量都是存在主存当中(类似于前面说的物理内存),每个线程都有自己的工作内存(类似于前面的高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。
  • Java内存模型经过长时间的验证修补和发展,JDK1.5之后,基本成熟和完善起来了。

Page10

内存间交互协议

  • lock:作用于主内存变量,将其标识为线程独占
  • unlock:作用于主内存变量,将线程独占释放
  • read:作用于主内存变量,把变量从主内存读到线程工作内存,之后是load
  • load:作用于工作内存变量,把之前read的值存入工作内存副本中
  • use:作用于工作内存变量,将其值传给执行引擎
  • assign:作用于工作内存变量,将从执行引擎收到的值赋给工作内存变量
  • store:作用于工作内存变量,将工作内存的值传送到主内存,之后是write
  • wirte:作用于主内存变量,将store操作得到的值存入主内存中

Page11

附加规则

  • 不允许read和load、store和write操作之一单独出现
  • 不允许一个线程丢弃它最近的assign操作,即变量在工作内存中改变后必须把该变化同步回主内存
  • 不允许一个线程无原因地(没有发生assign)把数据从工作内存同步回主内存
  • 一个新变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assgin)的变量,即对一个变量实施use、store操作之前,必须先执行了load和assgin。
  • 一个变量同一时刻只允许一条线程对其进行lock,执行unlock才能释放
  • 如对一个变量执行lock操作,则会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign初始化变量值
  • 如果一个变量没有被lock,则不能执行unlock,也不能unlock其他线程
  • 对一个变量执行unlock之前,必须把此变量同步回主内存(执行store、write)

Page12

volatile关键字

  • 保证变量对所有线程的可见性
  • 禁止指令重排序优化

Page13

指令重排序

  • 指令重排序(Instruction Reorder)——处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的

  • 有数据依赖性则不能进行指令重排序,如下表所示:

名称 代码示例 说明
写后读 a = 1;b = a; 写一个变量之后,再读这个位置。
写后写 a = 1;a = 2; 写一个变量之后,再写这个变量。
读后写 a = b;b = 1; 读一个变量之后,再写这个变量。

Page14

案例1:volatile自增操作

Java内存模型与线程

Page15

案例2:指令重排序优化

Java内存模型与线程

Page16

案例3:DCL单例模式

Java内存模型与线程

Page17

volatile规则小结

  • 在工作内存中,每次使用V前必须从主内存刷新最新的值,用于保证能看见其他线程对变量V做的修改(即read-read-use联动)
  • 在工作内存中,每次修改V之后必须立刻同步回主内存,用于保证其他线程可以看到自己对V做的修改(即assign-store-write联动)
  • volatile修饰的变量不会被指令重排序优化,保证代码执行顺序与程序顺序相同

Page18

happens-before原则

  • 程序次序规则:在一个线程内,按照程序逻辑执行
  • 管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作
  • 线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作
  • 线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测
  • 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
  • 对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始
  • 传递性:如果操作A先行发生于操作B,操作B先行发生于操作C,则A先行发生于操作C

Page19

先行发生原则判断示例

Java内存模型与线程

Page20

三大特征

  • 原子性
  • 可见性
  • 有序性

Page21

原子性

  • 在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作
  • read load assign use store write操作具有原子性
  • synchronized同步块也具有原子性
  • volatile无法保证原子性

Page22

思考:哪个操作具有原子性

  • x = 10
  • y = x
  • x++
  • x = x + 1

Page23

可见性

  • volatile可以保证可见性
  • synchronized
  • final
  • this引用逃逸

Page24

有序性

  • Java程序中天然的有序性可以总结为:如果在本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。前半句指线程内表现为串行的语义——Within Thread As-If-Serial Semantics;后半句指“指令重排序”现象和“工作内存与主内存同步延迟”现象。
  • volatile禁止指令重排序
  • synchronized保证线程间操作的有序性

Page25

Java线程

  • 线程实现
  • 线程调度
  • 线程状态

Page26

线程实现方式

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

Page27

1:1模型

Java内存模型与线程

图片摘自《深入理解Java虚拟机》

Page28

1:N模型

Java内存模型与线程

图片摘自《深入理解Java虚拟机》

Page29

N:M模型

Java内存模型与线程

Page30

Java线程实现方式

  • JDK1.2之前,使用用户线程方式实现
  • JDK1.2改为基于操作系统原生线程模型实现
  • 目前JVM线程的映射方式取决于操作系统支持哪种线程模型,不同平台不一定一致。如Windows、Linux上是一对一实现,即一条Java线程映射到一条轻量级进程中;而Solaris中同时支持一对一及多对多。
  • Java语言层是统一的,JVM封装了操作系统的不同

Page31

线程调度方式

  • 协同式线程调度——Cooperative Threads-Scheduling (如Windows 3.x)

  • 抢占式线程调度——Preemptive Threads-Scheduling (线程优先级)

Page32

Java线程状态

Java内存模型与线程

Page33

并发编程相关

  • sleep
  • join
  • yield
  • wait、notify、notifyAll
  • synchronized
  • volatile
  • Lock
  • ThreadLocal
  • 同步容器,如Vector、Stack、HashTable
  • 并发容器,如ConcurrentHashMap、CopyOnWriteArrayList
  • 阻塞队列BlockingQueue
  • 线程池
  • Callable、Future、FutureTask
  • ……

参考文献

  • 《深入理解Java虚拟机》