Java中的锁

Java中的锁_第1张图片

java中的锁遵循不同的分类方法,太多了,乐观锁/悲观锁,可重入锁/不可重入锁,有些第一遇到的话,可能还有点懵。刚好周末有时间学习下,总结和梳理下。

一总述

总的来说对java的锁有以下七种分类方法:

  • 悲观锁/乐观锁

  • 可重入锁/不可重入锁

  • 共享锁/互斥锁

  • 公平锁/非公平锁

  • 自旋锁/非自旋锁

  • 可中断锁/不可中断锁

  • 偏向锁/轻量级锁/重量级锁

二悲观锁和乐观锁

悲观锁比较悲观,在操作共享资源之前,先申请锁,再操作,保证在操作期间其他线程绝无干扰的可能。乐观锁,比较乐观,认为大部分情况下,资源是没有被占用的,它利用CAS机制,不占用资源,在满足条件(老的值没发生变化)的时候进行修改,如果没达到修改条件,则原来获取到的值已经变了,则重新获取修改。

Java中比较典型的有:悲观锁:同步的关键字和锁界面 同步的关键字锁,1.6版本之后有个升级的过程,也不一定就肯定是悲观锁,当其升级为重量级锁后才可以称为悲观锁。实现锁接口的ReentrantLock加锁加的就是悲观锁。

乐观锁 java中的Atomic *类锁就是乐观锁,通过CAS机制来更改或累加数字。

乐观锁在竞争不激烈的情况下,由于不需要像悲观锁一样存在线程的等待和唤醒,同样也无进行进行的切换,所以性能更好些。相反,如果竞争很激烈,临界区的代码又比较复杂比较耗时,乐观锁由于不释放资源,采用循环的方式一直去判断,由于长期CAS操作失败,CPU又没有释放,反而不如悲观锁。

三可重入锁和不可重入锁

可重入锁的意思是,如果一个线程获取了一个锁A,可以再次获取锁A。不可重入锁就是一个线程获取锁B后,如果要再次获取锁B必须释放掉,这个锁就是不可重入的。

java中的重入锁:已 同步和ReentrantLock是可重入锁已 同步通过monitorenter和monitorexit来实现加锁和解锁。monitorenter如果该monitor的计数为0,说明没有线程获取到此锁,就加1;如果monitor的计数为1,且线程是本线程,则可以重入,且计数+1,如果为其他线程占用,则分开;monitorexit对监视器计数-1,当监视器计数为0的时候,释放。

四共享锁/互斥锁

共享锁就是一个锁可以同时被多个线程拥有,它们之间是共享的;互斥锁或独占锁,就是一个锁只能被一个线程占用。;读和写和写和写之间,必须用独占锁来进行控制。

Java中的ReentrantReadWriteLock.ReadLock为共享锁;ReentrantReadWriteLock.WriteLock写锁为独占锁。

五公平锁/非公平锁

公平锁和非公平锁是指如果线程未获取到锁之后,是不是要进入到排序排序,是按照申请的先后依次得到锁,如果是的,就是公平锁;如果不是的,就是非公平锁。ReentrantLock可以设置公平锁或非公平锁,而且唯一的就是非公平锁,很奇怪,不是嘛。为什么要设置非公平锁,非公平锁也不是完全不按顺序,甚至在一定情况下可以插队。这个一定的情况下,某种一个线程A来申请锁的时候,正在占用锁的线程C刚好释放了锁,如果在非公平锁模式下,线程A不用排队,直接可以获取到锁。为什么要这样,为什么这样的性能更高。为什么性能更高,因为其他的等待锁的线程,处于休眠状态,要中断线程,进行切换切换也耗时的,这个时刻,也许我们的线程已经执行完毕了。

Java中的公平锁和非公平锁 公平锁:

new ReentrantLock(false) ;

非公平锁:

new ReentrantLock() ;

synchronized总结:非公平锁:优点:性能更好;缺点:可能会有个别线程一直获取不到锁的情况。公平锁:优点:等待中断,一定可以获取到;缺点:性能差,爆炸小。

六自旋锁/非自旋锁

自旋就是循环,就是在没有获取到锁之后,不进入两次,而是通过循环的方式再次去获取,此类Atomic *实质利用的就是自旋锁,核心逻辑是通过CAS和配合循环实现的,代码如下:

public final long getAndAddLong (Object var1,long var2, long var4){
    long var6;
    do {
        var6 = this.getLongVolatile(var1, var2);
    } while (!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
    return var6;
}

自旋锁,在我理解和乐观锁很像,乐观锁很多都是通过自旋来实现的。

七可中断锁/不可中断锁

而ReentrantLock锁是可中断锁,我们可以通过ReentrantLock中的lockInterruptibly()来中断锁,而lockInterruptibly()不必获取锁了。调用时,不可中断锁,意思是锁申请了就不能取消了,只是同步,一旦申请了,就不能中断。的时候,如果可以获取锁,则立即返回;如果没有获取到锁,则等待直到获取锁或发生了中断,这样比不能中断的同步和lock()方法更灵活。

八偏向锁/轻量级锁/重量级锁

偏向锁/轻量级锁/重量级锁是synchronized的锁的状态。JVM根据竞争的激烈程度不同将锁逐步升级。synchronized修饰的方法或代码块,调用前,对象初始状态处于无锁状态

偏向锁:如果来了一个线程,后续没有线程来了,其实没必要上锁,只要在Java对象的Markword中设置标记下就行了,这个就是偏向锁,偏向于这个线程,后续还是这个线程来的话,直接获取锁,这种性能最好。

轻量级锁:   如果有一个以上的线程来进入临界区,那么刚才的偏向锁就会升级,升级为轻量级锁,即通过自旋锁的方式来获取锁,JVM认为在竞争不激烈的当时,当时又有竞争,这时候用CAS配合自旋更合适,而不用进入双重。

重量级锁:当实际有很多线程来竞争的话,还利用轻量级锁的话,获取锁需要获取的时间比较长,而且占用着cpu。这种情况下轻量级锁就会升级到重量级锁,性能更稳定,cpu占用容量。

九诗词欣赏

赠范晔诗

陆凯 南北朝

折花逢驿使,寄与陇头人。
江南无所有,聊赠一枝春。

你可能感兴趣的:(Java中的锁)