java并发编程

3次阅读

共计 1988 个字符,预计需要花费 5 分钟才能阅读完成。

一. 程序,进程,线程

程序:代码实现了功能,就是程序,是静态的
进程:执行中的程序就是进程,是动态的。是操作系统分配资源的最小单位
线程:进程内的一个执行单元,是程序执行的最小单位

二. jvm(java 虚拟机)内存划分


1. 堆:存放对象实例
2. 方法区:存放类信息,常量,静态变量,jit(即时编译器——某个方法或代码块运行频繁的时候,认为这是热点代码。在字节码到机器码的过程中,就不再逐条解释,而是一次性编译热点代码,并把机器码缓存起来,以备下次使用)
3. 虚拟机栈:存放栈帧,每个方法的调用对应着栈帧的压入
4. 本地方法栈:为虚拟机使用到的 native 方法服务
5. 程序计数器:下一条指令所在单元地址

三. java 内存模型(JMM)

1. 概念
线程间通信模型,它是一种规范,是一种抽象的模型。将内存划分为主内存和工作空间。主内存存储共享信息,工作空间存放私有信息。线程修改私有的数据,直接在工作空间修改,对于共享的数据,先从主存读取存放到工作空间,修改后刷新会主存。
2.JMM 与对应的硬件模型

其中寄存器和 cache 相当于工作空间。工作空间中的数据可能存在于寄存器,cache,内存中。内存中的数据亦是如此。

3. 缓存一致性问题
缓存一致性:就是高速缓存中的共享数据保持一致性的机制
(1)解决方案:
①总线加锁:CPU 和其它部件进行通信是通过总线来进行的,总线加锁可以保证每次只有一个 CPU 可以使用内存中的变量。
②缓存一致性协议(MESI):CPU 中每个缓存行使用 4 种状态进行标记 M:modified 修改 E:Exclusive 独享 S:shared 共享 I:Invalid 无效的 一个写请求只能在该缓存行是 M 或 E 状态才能被执行,如果缓存行处于 S 状态,必须先将其它缓存种该缓存行编程 Invalid 状态

四. 并发编程的 3 个特性

1. 原子性:一个或多个操作为一个整体,要么全执行,要么全不执行
(1)JMM 确保原子性:锁,synchronized 来确保
2. 可见性:多个线程访问同一变量时,一个线程修改了这个变量的值,其它线程能够立即看到修改的值
(1)JMM 确保可见性:锁,sychronized,volatile
3. 有序性:程序执行的顺序按代码先后执行(因为可能发生指令重排序)
(1)JMM 确保有序性:
①一个线程中的每个操作,happens-before 于该线程中的任意后续操作
②程序顺序规则:对一个锁的解锁,happens-before 于随后对这个锁的加锁
③监视器锁规则:对一个 volatile 域的写,happens-before 于任意后续对这个 volatile 域的读
④传递规则:如果 A happens-before B,且 B happens-before C,那么 A happens-before C
⑤start()规则:如果线程 A 执行操作 ThreadB.start()(启动线程 B),那么 A 线程的 ThreadB.start() 操作 happens-before 于线程 B 中的任意操作
⑥join()规则:如果线程 A 执行操作 ThreadB.join() 并成功返回,那么线程 B 中的任意操作 happens-before 于线程 A 从 ThreadB.join()操作成功返回
⑦程序中断规则:对线程 interrupt() 的调用 happen—before 发生于被中断线程的代码检测到中断时的事件
⑧对象终结规则:一个对象的初始化完成(构造函数执行结束)happen—before 它的 finalize()方法的开始

五.synchronized

synchronized 可以用在类和方法上

1. 对象的锁,即 monitor 可以理解为对象的标志位,这个标志存放在 java 对象的对象头。对象头里的 markword 存储结构如下:


2. 锁一共有 4 种状态:无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态。锁可以升级但不能降级

3. 轻量级锁
如果没有实际的锁竞争,那么申请重量级锁是浪费性能的。加锁过程:(1)jvm 在当前线程的栈帧中创建用于存储锁记录的空间(2)将对象头中的 mark word 复制到锁记录中(3)尝试使用 cas 将对象头中的 mark word 替换为指向锁记录的指针,成功则获得锁。失败尝试使用自旋操作来获取锁

4. 偏向锁:是对轻量级锁的一种优化。它适用于没有竞争且只有一个线程使用锁的情况。(轻量级锁每次申请都需要一次 cas,偏向锁只初始化需要一次)偏向锁对象第一次被线程访问时使用 CAS 把这个线程 id 记录在对象的 mark word 中,同时是否偏向锁标志位置 1,以后该线程进入和退出同步块时不需要进行加锁和解锁,只需要简单的测试一下对象的 mark word 中是否存储着该线程 id

5.cas:compare and swap , 内存中的值,预期值,新值。内存中的值与预期值相等时才将内存中的值改为新值,否则什么也不做。对于 JUC 和 synchronized 的优化都是建立在 cas 上的

正文完
 0