1、异步编程模型---侧重并发和吞吐量
两个线程t1,t2各自执行各自的,t1不管t2,t2不管t1,互不等待,多线程并发,效率较高
2、同步编程模型---侧重安全
t1执行的时候,必须等t2执行结束,t2执行的时候,必须等t1执行结束,两个线程之间发生等待关系,线程串行排队执行
异步就是并发,同步就是排队。
(1)线程并发
(2)有共享数据
(3)共享数据有修改行为
(1)互斥同步----synchronized lock
(2)非阻塞同步---乐观的并发策略,不需要线程挂起 :CAS
(3)无同步---可重入代码,ThreadLocal
(1)临界区
(2)互斥量
(3)管程(Monitor 监视器)
(4)信号量
(5)CAS(比较并交换)
确保本执行过程中内存值没被其他线程修改过
(1)执行原理
(2)synchronized加锁的三种情况
synchronized(要锁的共享对象){
对象相关的同步代码块
}
【注】方法内要锁的共享对象一般为this,也可为其他全局变量。
若当前对象是方法的局部变量不可用this。(非共享,线程私有)
run(){
synchronized(要锁的共享对象){
对象相关的同步代码块
}
}
【注】此处不可以为this,因为此处this表当前对象--线程对象,一个线程对应一个自己对象,非共享对象
缺点: 扩大了同步范围,同步了整个方法,执行效率降低,同步代码越少,效率越高
public synchronized void 方法名(){ 相关同步操作 }
优点:代码写的少了,节简
缺点:锁的只能是this当前类对象,不能是其他全局变量,不灵活
(1)基本操作
加锁:对象.lock
解锁:对象.unlock
(2)与synchronized不同点
ReentrantLock可以通过构造函数实现公平锁. new RenentrantLock(boolean fair)
synchronized的是非公平锁-----锁释放后,任何线程都有机会获得锁
synchronized----只能用wait和notify实现一个隐含条件
如果有,则进行其他的补偿(最常见是不断的重试)
1)测试并设置(test-and-set)
2)获取并增加
3)交换
4)比较并交换(CAS) jdk5后
5)加载链接/条件储存(Load-Linked/Store-Conditional LL/SC)
使用ThreadLocalMap存储共享变量值
(1) 悲观锁----排他锁
在开始改变此对象之前就将该对象锁住,并且直到提交了所做的更改之后才释放锁。
排队进行
乐观锁----共享锁
假设不会发生并发冲突。轻易不加锁。
(2)公平锁---按申请顺序依次获得锁
非公平锁---通过竞争获得锁
(1)无锁状态
(2)偏向锁状态 01 锁标志位
<1>目的:大多数情况下锁不仅不存在多线程竞争,而且总是由同一线程多次获得
为了让线程获得锁的代价更低而引入了偏向锁
<2>特点:锁偏向于第一个获得它的线程,
当一个线程访问同步快并获取锁时,会在对象头和栈帧中锁记录里存储锁偏向的线程ID
若执行过程中一直没有被其他线程获取,则持有偏向锁的线程将不需要同步
一旦有其他线程尝试获取锁,偏向模式立即结束
<3>适用场景:只有一个线程访问同步代码块
(3)轻量级锁状态00
<1>目的:为了在没有竞争前提下,减少重量级锁适用操作系统互斥量产生的性能消耗(加锁减锁)
<2>特点:使用CAS尝试将对象的Mark Word更新为锁记录的指针
成功--线程拥有锁;失败--自旋等待
当出现两条以上线程争同一个锁,轻量级锁转换成重量级锁
<3> 适用场景:两个线程
(4)重量级锁状态10
<1> 特点:线程竞争不使用自旋,不消耗cpu,但是线程会阻塞,响应时间慢
至少包含一个竞争锁的队列,和一个信号阻塞队列(wait队列),前者负责做互斥,后一个用于做线程同步
<2>适用场景:两个以上线程
(1)自旋锁
(2)自适应自旋
自适应自旋意味着自旋的时间不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。
(3)锁清除:
虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。锁消除的主要判定依据来源于逃逸分析的数据支持。
(4)锁粗化:
如果虚拟机探测到有一系列连续操作都对同一个对象反复加锁和解锁,将会把加锁同步的范围扩展(粗化)到整个操作序列的外部。
(5)锁升级
偏向锁---->轻量级锁----->重量级锁
参考:java线程安全和锁机制详解_jiangbb8686的博客-CSDN博客
Java 并发 - 理论基础 | Java 全栈知识体系