读《java并发编程的艺术》前三章总结(持续迭代、优化)

最近在读《java并发编程的艺术》感觉受益良多,摘抄书中部分语录,进行个大致总结,用来面试,建议阅读全书!!!


2021年08月22日

前三章

在这里插入图片描述

内存模型

JMM定义

线程之间的共享变量存储在主内存中,每个线程都有一个私有本地内存,本地内存中存储了该线程以读/写共享变量的副本,本地内存是JMM的抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。

happens-before(一种关系:之前)

只要求前一个操作的结果对后一个操作可见,并不要求前一个操作在第二个操作之前。

重排序

编译器,处理器为了优化性能对指令重排序,存在数据依赖关系的数据不会被重排序。

as-if-serial(像串行)

单线程环境下,不管怎么重排序,最终结果都是一样的。

volatile内存语义防止重排序

总结:每编译器不会对volatile读与volatile读后面的任意操作作重排序,编译器不会对volatile写与volatile写之前的任意内存操作重排序。
原理: 在每个volatile写操作之前插入Storestore屏障:之前的写在当前写之前。
在每个volatile写之后插入StoreLoad屏障:当前写在之后读之前。
在每个volatile读之后插入Loadload屏障:当前读在之后的读之前。
在每个colatile读之后插入Loadstore屏障:当前都在之后写之前。 编辑器可以对屏障进行优化比如:
volatile读->Loadload->volatile读 中间可以省略Loadstore屏障,因为两个读之间没有写。

锁内存语义防止重排序

CAS:如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的更新值。此操作具有volatile读和写的内存语义。
利用volatile变量的写-读所具有的内存语义。 利用CAS所附带的volatile读和volatile写的内存语义

final内存语义防止重排序

写: JMM禁止编译器把final域的写重排序到构造函数之外。
编译器会把final域的写之后,构造器return之前,插入一个StoreStore屏障。final的写在之后的写之前。
在对象引用为任意线程可见之前,对象final域已经被初始化过了,普通域没有这个保证。 读:
编译器会在读final域操作前面插入一个LoadLoad屏障。之前的读先于final的读。
在读一个对象final域之前,一定会先读包含这个final域的对象引用。

Volatile怎么读可见?

定义

是一个轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性(一个线程修改了一个共享变量时,另一个线程能读到这个修改的值)

CPU说明

1、内存屏障:实现一组指令对内存的操作时顺序限制 2、缓冲行:缓存中分配的最小单位,处理器加载缓存时会加载整个缓存行
3、原子操作:不可中断的一个或者一些列的操作 4、缓存行填充:处理器读取操作数据可缓存时,就会读取缓存行到适当缓存
5、缓存命中:如果缓存行填充操作的内存地址仍然是下次处理器的访问地址时,处理器读取缓存
6、写命中:写入数据时检查这个缓存的内存地址是否存在缓存行,存在有效的缓存行,将数据写入缓存行
7、写缺失:一个有效的缓存行写入到不存在的内存区域

Volatile原理

处理器不直接和内存进行通信,将内存数据读到内部缓存,被Volatile修饰的共享变量,进行写操作时会多出第二行汇编代码(第二行加有lock前缀标识),在多处理器情况下实现缓存一致性(会阻止同时两个以上处理器缓存写入内存)协议主要是依赖处理器的嗅探技术保证内部缓存和系统缓存和其他处理器缓存的数据在一个总线上。
lock前缀标识会发生两件事:
第一件:将当前处理器缓存行的数据写回到系统内存中 lock信号保证处理器在此信号期间独占任何共享内存或者锁定这块区域的缓存并写回到到内存,使用缓存一致性确保修改的原子性此操作被称为【缓存锁定】
第二件:这个写回内存的操作会使在其他CPU里缓存了改地址的数据无效 通过嗅探一个处理器检测其他处理器打算写入一个共享状态数据到内存地址,那么正在嗅探的处理器将使自己的缓存行无效,在下次执行访问这个共享的内存地址时强制执行缓存行填充

Synchronized怎么锁升级?

定义

synchronized是一个对象锁,锁的对象基于synchronized所在位置
普通同步方法,锁是当前实例对象;
静态同步方法,锁的是实例的类对象;
同步方法块,锁的synchronized(Object)括号中的对象;

对象头

synchronized是存在锁住的对象的对象头中的Mark Word用来存储hashCode或者锁信息

锁升级

JDK1.6之后分为四种状态依次是:无锁(0:无锁标记),偏向锁(线程ID|纪元|1:是否是偏向锁|01:偏向锁),轻量级锁(指向栈的指针|00:轻量级锁),重量级锁(指向相互斥量的指针|10:重量级锁)。锁不可降级!!!

偏向锁:

偏向锁定义: 是指当一段同步代码一直被同一个线程所访问时,即不存在多个线程的竞争时,那么该线程在后续访问时便会自动获得锁,从而降低获取锁带来的消耗,即提高性能。
偏向锁加锁: 一个线程获取锁时就把线程ID写入Mark Word并且更改锁状态为1
偏向锁撤销: 竞争锁时会进行的操作,当其他线程尝试竞争偏向锁时,会在安全时(同步代码块执行完成),暂停持有锁的线程,将对象头设置为无锁或者偏向于其他线程。
具体操作: 在对象头和栈帧中记录存储锁偏向的线程ID,之后此线程再次进入和退出不需要使用CAS来加锁和解锁,只需要检测Mark Word中是否存储当前线程ID。如果失败就检测Mark Word中锁标记是否为1(1:标识为偏向锁),撤销时将锁状态设置为0(0:标识为无锁)或者记录为其他偏向线程。

轻量级锁:

轻量级锁定义: 是指当锁是偏向锁的时候,却被另外的线程所访问,此时偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能。
轻量级锁加锁: 线程访问同步方法时JVM在当前线程的栈帧中记录Mark Word,然后用CAS将Mark Word替换为指向锁记录的指针,如果成功获取锁,不成功就尝试使用自旋(存在次数限制,超过就膨胀)获得锁。
轻量级锁解锁: 解锁时使用CAS操作将Mark Word替换回到对象头,如果成功表示没有竞争,如果失败存在竞争则膨胀为重量级锁。
具体操作: 轻量级锁在多线程竞争下获得Mark Word的线程会把数据原始状态hashcode|0(无锁)|01(偏向锁)改为指向栈的指针|00(轻量级锁)这时未获得的线程会自选尝试获取锁,当尝试一定次数还未获得锁时,会将Mark Word的状态修改为重量级锁(指向相互斥量的指针|10(重量级锁))当前未获得锁的线程开始阻塞,获得锁的线程执行完后直接释放锁不在把轻量级锁的状态写回Mark Word,并且唤醒等待的线程。

重量级锁:

偏向锁定义: synchronized最终状态,会使抢不到锁的线程阻塞。

2021年08月31日更新

并发编程基础

线程定义:

操作系统最小单位是线程,线程拥有各自的计数器、堆、栈、局部变量等属性。并且可以访问共享的内存变量。处理器在这些线程上告诉切换,让使用者感觉这些线程在同时执行。

线程状态:

初始状态:new线程创建还没有调用start()方法
运行状态:runnable就绪和运行两种状态分为运行中
阻塞状态:blocked线程阻塞于锁 等待获取锁synchronized!!!lock接口中的方法状态是等待状态
等待状态:waiting等待其他线程做出对应动作 wait() lock
超时等待状态:time_waiting等待状态,可以在一定时间内自行返回 sleep、wait(long)
终止状态:terminated线程执行完毕
中断状态:interrupt线程被迫中止

线程执行状态:

线程创建后执行start()开始运行。当线程执行wait()方法后,线程等待状态,需要其他线程通知才可以返回到运行状态,超时状态相当于在等待状态基础上增加了超时限制,也就是超时时间到达时回到运行状态。当线程调用同步方法时在没有获取到锁的情况下,线程会进入阻塞状态,线程执行完成后进入终止状态,抛出异常或者调用interrupt()方法会进入中断状态也就是终止状态。

等待/通知机制

wait():释放当前获得的锁,然后线程状态为等待状态,只有其他线程通知或者被中断才会返回。会进入等待队列
wait(long):释放当前获得的锁,然后线程状态为超时等待状态,一定时间没有通知就自动返回。会进入等待队列
notify():通知一个该对象正在等待的线程可以执行,然后当前锁释放后,等待的线程获取锁后从wait()方法处返回。将等待队列中一个线程移动到同步队列。
notifyAll():通知所有在该对象正在等待的线程可以执行,然后当前锁释放后,等待的线程获取锁后从wait()方法处返回。将等待队列中所有线程移动到同步队列。

Thread.join()

当前线程调用AThread.join()后当前线程等待AThread执行完成后,从AThread.join()处返回,join(long)是一定时间内线程未中止,则从join(long)处自动返回。

ThreadLocal

线程变量,同一线程下set(T)设置的值,在get()方法获取到原先的值。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值