Java线程---锁机制
Syschronized同步锁
同一时刻,只有一个线程能持有这把锁,保证代码的线程安全
Syschronized关键字的用法
1.修饰实例方法---->this为当前方法的调用对象
-
对加锁的代码块限制了线程访问,并不影响其他线程访问没加锁的代码块
-
子类若重写父类syschronized修饰的方法,线程是否安全以子类重写的为准
-
定义接口方法时,不能使用syschronized关键字
-
构造方法不能使用syschronized关键字,但可以使用syschronized代码块来进行同步。
-
离开syschronized代码块后,该线程所持有的锁,会自动释放。
2.修饰静态方法---->Class对象
3.修饰代码块---->某个指定的Java对象
Syschronized实现原理
Java5以前---------Syschronized同步锁在加锁和解锁过程中,依赖于操作系统互斥锁(Mutex LOCK)所实现的锁。属于重量级锁。
Java6之后---------采用monitor机制和JVM指令实现
-
Monitor机制
每个java对象都可成为Monitor监视器,分为三部分
Wait Set线程等待区---等待唤醒,重新尝试获取Monitor
Entry List 线程阻塞区---等待锁释放
Owner 线程拥有者-----执行同步代码块或者同步方法中的代码
-
JVM指令
-
Syschronized修饰方法,方法常量池添加名为ACC_Syschronized的标志,调用方法时,需要先获取当前this对象的监视器才能继续,方法执行完后释放监视器锁
-
Syschronized修饰代码块,通过monitorenter和monitorexit两个指令实现。
monitorenter指令插入到同步代码块的开始位置,尝试获取Monitor监视器的所有权;monitorexit指令插入到方法结束处或异常处,JVM保证每个monitorenter必须有对应的monitorexit。
-
Syschronized底层实现✔
Syschronized保证线程安全,保证原子性
-
通过monitorenter/monitorexit指令实现同步
-
通过对象内部---监视器(monitor)来实现的
监视器(monitor)
监视器-------充当锁的的对象对当前竞争锁的所有线程进行管理
锁优化
-
自旋锁:线程等待一段时间,不会立即挂起,看持有锁的线程是否很快释放锁
-
适应性自旋锁:自旋次数不固定,根据前一次在同一个锁上的自选时间及锁的拥有者状态决定
-
锁粗化:将一系列连续加锁、解锁操作,合并为一个较大范围的加锁解锁操作
-
锁消除:JVM检测到不可能存在共享数据竞争时,会对同步锁进行锁消除
锁升级/锁膨胀!!!
偏向锁------>轻量级锁------>重量级锁
偏向锁:
加锁的代码从始至终只有一个线程在调用,如果发现有多于一个线程交替执行同步块情况,再升级成轻量级锁。
轻量级锁:
不支持"并发",出现并发情况即多个线程同一时间访问同一把锁的情况,就会升级为重量级锁
-
轻量级锁加锁:对象锁处于无锁状态,虚拟机在线程的帧栈中建立Local Record(锁记录)存储锁对象目前的Mark Word,将对象的Mark Word中的指向栈中锁记录的指针更新为Local Record指针,并将Local Record里的owner指针指向对象的Mark Word。
-
轻量级锁解锁:通过CAS指令,将线程中复制的Displaced Mark Word对象替换当前的Mark Word。
(获取锁采用自旋方式,通过循环获取锁避免线程阻塞)
重量级锁:
依赖于操作系统互斥锁所实现的锁,需要从用户态转换为内核态,切换成本高。
ReentrantLock锁
ReentrantLock和Syschronized一样,都是可重入锁,即一个线程可以多次获取同一个锁。
ReentrantLock与Syschronized的区别
-
锁实现机制:ReentrantLock基于AQS框架;Syschronized基于监视器Monitor和JVM指令实现。
-
获取锁方式:ReentrantLock可通过tryLock()尝试获取锁,更灵活且在失败的时候不会导致死锁;Syschronized则是线程抢占模型
-
释放锁方式:ReentrantLock必须显示通过unlock()释放锁,Syschronized可自动释放锁
-
锁的类型:ReentrantLock支持公平锁和非公平锁;Syschronized只支持非公平锁。
ReentrantLock内部结构
三个内部类
-
Sync
-
NonfairSync继承Sync类:非公平策略获取锁(默认)
-
FairSync继承Sync类:公平策略获取锁,当资源空闲会判断Sync队列中是否有等待时间更长的线程,如存在,则入队尾。
通过ReentrantLock(boolean)构造函数,传递参数指定采取策略 true---公平策略 false---非公平策略