并发基础
JMM
线程通信
消息传递
AQS
AbstractQueuedSynchronizer同步器
- 用来构建锁或者其它同步组件的基础框架,使用int表示同步状态,通过内置的fifo队列完成资源获取线程的排队工作
- getstate
- setstate
- compareAndSetState
- 实现
- 同步队列
- 双链表
- 基于CAS死循环设置尾节点
- 通过获取同步状态成功设置首节点
- 每个节点自旋观察是否获取到同步状态
- 双链表
- 独占同步状态获取与释放
- 获取失败生成节点
- 同步队列
CLH同步队列
同步状态获取释放
线程阻塞和唤醒
CAS
compare and swap
原子操作实现原理
相关术语
- 缓存行 cache line
- 缓存最小操作单位
- 比较并交换 CAS
- cpu流水线
- 内存顺序冲突
- 假共享引起
- 多个cpu同时修改同一个缓存行的不同部分引起其中一个cpu的操作无效
- cpu必须清空流水线
- 假共享引起
处理器实现原子操作
- 处理器读取一个字节时,其它处理器不能访问这个字节的内存地址
- 总线锁
- lock信号,一个处理器输入此信号,其它处理器请求将被阻塞,处理器独占共享内存,其他处理器不能操作其它内存地址的数据,开销较大
- 缓存锁
- 内存区域如果被缓存在处理器的缓存行中,执行锁操作写回到内存时,修改内部的内存地址,并允许由它的缓存一致性机制保证操作的原子性,当其他处理写回被锁定的缓存行的数据时,会使缓存行无效
- 缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域
- 不适用
- 数据不能缓存在处理器内部或者跨多个缓存行
- 调用总线锁定
- 不支持处理器缓存的机器
- 数据不能缓存在处理器内部或者跨多个缓存行
- 内存区域如果被缓存在处理器的缓存行中,执行锁操作写回到内存时,修改内部的内存地址,并允许由它的缓存一致性机制保证操作的原子性,当其他处理写回被锁定的缓存行的数据时,会使缓存行无效
- 总线锁
jvm实现
- 锁
- CAS
- CMPXCHAG指令
- 问题
- ABA
- atomicStampedRefrence
- compareandSet
- (V expectedReference,
-
V newReference,
-
int expectedStamp,
-
int newStamp)
- compareandSet
- atomicStampedRefrence
- 循环时间长开销大
- pause
- 延迟流水线指令,减少cpu消耗
- 锁
- 合并变量
- atomicRefrence
- pause
- 只能保证一个共享变量的原子操作
- ABA
对象头
Mark Word
- 存储对象的hashcode或者锁信息等
- 对象hashcode
- 对象分代年龄 4
- 是否偏向锁 1
- 锁标志位 0
- 00 轻量级锁
- 指向栈中锁记录的指针
- 01偏向锁
- 线程ID Epoch 分代年龄 1 01
- 10重量级锁
- 指向互斥量的指针
- 11 gc标志
- 01 无锁
- 对象的HashCode 分代年龄 0 01
- 00 轻量级锁
class metadata address
- 存储到对象类型数据的指针
array length
- 数据长度(如果当前对象时数组)
锁升级与对比
偏向锁
- 等到竞争才释放锁
- 撤销偏向锁后恢复到未锁定(标志位为“01”)或轻量级锁(标志位为“00”)的状态。
轻量级
- 未获取锁的线程会自旋获取锁,存在竞争膨胀成重量级锁,持有轻量级锁的线程会解锁失败
重量级锁
- 未获得锁的线程获取锁时会被阻塞
多线程
进程是资源分配的最小单位,线程是CPU调度的最小单位
好处
- 更多处理器核心
- 更快相应
- 更好编程模型
状态
- NEW
- new了一个实例
- Daemon
- 支持型线程,用作程序中后台调度以及支持性工作
- USER
- Daemon
- new了一个实例
- RUNNABLE
- READY
- 就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态。
- 调用线程的start()方法,此线程进入就绪状态。
- 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
- 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
- 锁池里的线程拿到对象锁后,进入就绪状态。
- RUNNING
- 线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。
- READY
- BLOCK
- 阻塞,线程阻塞于锁
- 阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
- 阻塞,线程阻塞于锁
- WAITING
- 等待,当前线程需要等待其它线程做出一些特点动作,通知或者中断
- 处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。
- 释放对象锁,sleep不会
- 等待,当前线程需要等待其它线程做出一些特点动作,通知或者中断
- TIME_WAITING
- 执行时间自行返回
- TERMINATED
- 方法对比
- Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。
- Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
- t.join()/t.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程进入就绪状态。
- obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
- obj.notify()唤醒在此对象监视器上等待的单个线程,使其从wait方法返回,返回的前提是该线程获取到了对象的锁,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。
- waitnotify运行过程
- notify会把等待队列中的线程移到同步队列
- waitnotify运行过程
- 原文:https://blog.csdn.net/pange1991/article/details/53860651?utm_source=copy
线程间通信
线程池
- 消除线程的频繁创建消亡带来的开销,面对过量任务平缓劣化
XMind: ZEN - Trial Version