原子操作类其实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 平时存储这个对象的哈希码,分代年龄
- 当加锁时这些信息就根据情况被替换为 标记位,线程锁记录指针,重量级锁指针,线程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(重量锁)成功(加锁)-......重量级锁我们这里也来简单介绍一下重量级锁的优化方法:
- 重量级锁竞争的时候,还可以使用自旋来进行优化
- 如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞
- 在 Java 6 之后自旋锁是自适应的
- 比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋
推荐阅读
- Linux学习环境搭建流程
- Archlinux + Dwm 配置流程
- 关于入门深度学习mnist数据集前向计算的记录
- 四十 Java开发学习----MyBatisPlus入门案例与简介
- jvm双亲委派机制详解
- FHE学习笔记 #2 多项式环
- django-environ学习
- 万字详解JVM,让你一文吃透
- 论文笔记 - GRAD-MATCH: A Gradient Matching Based Data Subset Selection For Efficient Learning
- 论文笔记 - SIMILAR: Submodular Information Measures Based Active Learning In Realistic Scenarios