java锁
2020-06-13 15:22:32 0 举报
AI智能生成
java 锁机制
作者其他创作
大纲/内容
乐观锁VS悲观锁
乐观锁
CAS算法
原子类的自增操作
AtomicInteger
ABA问题
解决方法:版本号
悲观锁
Synchronized关键字
在操作同步资源之前需要给同步资源先加锁,这把锁就是存在Java对象头里
通过Monitor(称为内部锁)来实现线程同步,Monitor是依赖于底层的操作系统的Mutex Lock(互斥锁)来实现的线程同步
Lock类的实现类
自旋锁VS适应性自旋锁
自旋锁
-XX:+UseSpinning 默认开启
实现原理同样也是CAS,AtomicInteger中调用unsafe进行自增操作的源码中的do-while循环就是一个自旋操作
自旋限定次数默认是10次,可以使用-XX:PreBlockSpin
适应性自旋锁
如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也是很有可能再次成功,进而它将允许自旋等待持续相对更长的时间。如果对于某个锁,自旋很少成功获得过,那在以后尝试获取这个锁时将可能省略掉自旋过程,直接阻塞线程,避免浪费处理器资源。
无锁 VS 偏向锁 VS 轻量级锁 VS 重量级锁
无锁
没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功
CAS原理及应用即是无锁的实现
偏向锁
一段同步代码一直被一个线程所访问,那么该线程会自动获取锁
当一个线程访问同步代码块并获取锁时,会在Mark Word里存储锁偏向的线程ID。在线程进入和退出同步块时加锁和解锁是靠检测Mark Word里是否存储着指向当前线程的偏向锁。
偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令即可
-XX:-UseBiasedLocking=false来关闭,默认开启
轻量级锁
当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞
在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,然后拷贝对象头中的Mark Word复制到锁记录中。拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock Record里的owner指针指向对象的Mark Word。如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,表示此对象处于轻量级锁定状态。
重量级锁
若当前只有一个等待线程,则该线程通过自旋进行等待。但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁。
将除了拥有锁的线程以外的线程都阻塞
公平锁 VS 非公平锁
公平锁
非公平锁
ReentrantLock
内部类Sync,Sync继承AQS(AbstractQueuedSynchronizer)
默认使用非公平锁
公平锁FairSync和非公平锁NonfairSync两个子类
两个子类lock()方法唯一的区别就在于公平锁在获取同步状态时多了一个限制条件:hasQueuedPredecessors()。
hasQueuedPredecessors():主要是判断当前线程是否位于同步队列中的第一个。如果是则返回true,否则返回false。
可重入锁 VS 非可重入锁
一个优点是可一定程度避免死锁
Java中ReentrantLock和synchronized都是可重入锁
重入锁ReentrantLock以及非可重入锁NonReentrantLock
都继承父类AQS,其父类AQS中维护了一个同步状态status来计数重入次数,status初始值为0。当线程尝试获取锁时,可重入锁先尝试获取并更新status值,如果status == 0表示没有其他线程在执行同步代码,则把status置为1,当前线程开始执行。如果status != 0,则判断当前线程是否是获取到这个锁的线程,如果是的话执行status+1,且当前线程可以再次获取锁。而非可重入锁是直接去获取并尝试更新当前status的值,如果status != 0的话会导致其获取锁失败,当前线程阻塞。释放锁时,可重入锁同样先获取当前status的值,在当前线程是持有锁的线程的前提下。如果status-1 == 0,则表示当前线程所有重复获取锁的操作都已经执行完毕,然后该线程才会真正释放锁。而非可重入锁则是在确定当前线程是持有锁的线程之后,直接将status置为0,将锁释放。
可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞
非可重入锁
独享锁 VS 共享锁
独享锁
指该锁一次只能被一个线程所持有。获得排它锁的线程即能读数据又能修改数据。
JDK中的synchronized和JUC中Lock的实现类就是互斥锁。
ReentrantLock
虽然有公平锁和非公平锁两种,但是它们无论读操作还是写操作添加的都是独享锁
共享锁
指该锁可被多个线程所持有。获得共享锁的线程只能读数据,不能修改数据。
独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。
ReentrantReadWriteLock
有两把锁:ReadLock和WriteLock
读写锁靠内部类Sync实现的锁。Sync是AQS的一个子类
读锁是共享锁,写锁是独享锁。
0 条评论
下一页