JVM学习笔记——内存模型篇( 四 )

原子操作类其实JUC中为我们提供了原子操作类:

  • 可以提供线程安全的操作,例如:AtomicInteger、AtomicBoolean等,它们底层就是采用 CAS 技术 + volatile 来实现的 。
我们采用改写之前的一个例子来进行展示:
package cn.itcast.jvm.t4.avo;import java.util.concurrent.atomic.AtomicInteger;public class Demo4_4 {// 创建原子整数对象private static AtomicInteger i = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int j = 0; j < 5000; j++) {i.getAndIncrement();// 获取并且自增i++// i.incrementAndGet();自增并且获取++i}});Thread t2 = new Thread(() -> {for (int j = 0; j < 5000; j++) {i.getAndDecrement(); // 获取并且自减i--}});t1.start();t2.start();t1.join();t2.join();System.out.println(i);}}synchronized 优化这一小节我们来详细介绍一下synchronized的优化部分
Mark Word我们首先来介绍一个概念:
  • Java HotSpot 虚拟机中,每个对象都有对象头(包括 class 指针和 Mark Word) 。
那么我们主要需要这个Mark Word来存储信息:
  • Mark Word 平时存储这个对象的哈希码,分代年龄
  • 当加锁时这些信息就根据情况被替换为 标记位,线程锁记录指针,重量级锁指针,线程ID 等内容
轻量级锁首先我们先来介绍一下轻量级锁:
  • 如果一个对象虽然有多线程访问,但多线程访问的时间是错开的(也就是没有竞争),那么可以使用轻量级锁来优化 。
我们通过一个简单案例展示:
static Object obj = new Object();public static void method1() {synchronized( obj ) {// 同步块 Amethod2(); }}public static void method2() {synchronized( obj ) {// 同步块 B}}我们会发现即使上述为两个锁,但是同时都属于当前主线程下,并且是按顺序执行,这是就采用了轻量级锁
我们通过一个表格写出其具体流程:
线程 1对象 Mark Word线程 2访问同步块 A,把 Mark 复制到 线程 1 的锁记录01(无锁)-CAS 修改 Mark 为线程 1 锁记录 地址01(无锁)-成功(加锁)00(轻量锁)线程 1 锁记录地址-执行同步块 A00(轻量锁)线程 1 锁记录地址-访问同步块 B,把 Mark 复制到 线程 1 的锁记录00(轻量锁)线程 1 锁记录地址-CAS 修改 Mark 为线程 1 锁记录 地址00(轻量锁)线程 1 锁记录地址-失败(发现是自己的锁)00(轻量锁)线程 1 锁记录地址-锁重入00(轻量锁)线程 1 锁记录地址-执行同步块 B00(轻量锁)线程 1 锁记录地址-同步块 B 执行完毕00(轻量锁)线程 1 锁记录地址-同步块 A 执行完毕00(轻量锁)线程 1 锁记录地址-成功(解锁)01(无锁)--01(无锁)访问同步块 A , 把 Mark 复制到 线程 2 的锁记录-01(无锁)CAS 修改 Mark 为线程 2 锁记录 地址-00(轻量锁)线程 2 锁记录地址成功(加锁)-......锁的膨胀我们同样先来介绍锁膨胀的概念:
  • 如果在尝试加轻量级锁的过程中,CAS 操作无法成功
  • 这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争) , 这时需要进行锁膨胀,将轻量级锁变为重量级锁 。
我们直接给出一个表格写出其具体流程:
程 1对象 Mark线程 2访问同步块,把 Mark 复制到线程 1 的锁记录01(无锁)-CAS 修改 Mark 为线程 1 锁记录地 址01(无锁)-成功(加锁)00(轻量锁)线程 1 锁 记录地址-执行同步块00(轻量锁)线程 1 锁 记录地址-执行同步块00(轻量锁)线程 1 锁 记录地址访问同步块,把 Mark 复制 到线程 2执行同步块00(轻量锁)线程 1 锁 记录地址CAS 修改 Mark 为线程 2 锁 记录地址执行同步块00(轻量锁)线程 1 锁 记录地址失败(发现别人已经占了 锁)执行同步块00(轻量锁)线程 1 锁 记录地址CAS 修改 Mark 为重量锁执行同步块10(重量锁)重量锁指 针阻塞中执行完毕10(重量锁)重量锁指 针阻塞中失败(解锁)10(重量锁)重量锁指 针阻塞中释放重量锁 , 唤起阻塞线程竞争01(无锁)阻塞中-10(重量锁)竞争重量锁-10(重量锁)成功(加锁)-......重量级锁我们这里也来简单介绍一下重量级锁的优化方法:
  • 重量级锁竞争的时候,还可以使用自旋来进行优化
  • 如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞
我们对自旋进行简单补充: