1. 详解java并发之重入锁-ReentrantLock

     更新时间:2019年03月13日 15:37:28   作者?#21495;?#34382;。。   我要评论

    这篇文章主要介绍了java并发之重入锁-ReentrantLock,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    前言

    目前主流的锁有两种,一种是synchronized,另一种就是ReentrantLock,JDK优化到现在目前为止synchronized的性能已经和重入锁不分伯仲了,但是重入锁的功能和灵活性要比这个关键字多的多,所以重入锁是可以完全替代synchronized关键字的。下面就来介绍这个重入锁。

    正文

    ReentrantLock重入锁是Lock接口里最重要的实现,也是在?#23548;?#24320;发中应用最多的一个,我这篇文章更接近?#23548;?#24320;发的应用场景,为开发者提供直接上手应用。所以不是所有方法我都讲解,有些冷门的方法我不会介绍或一句带过。

    一、首先先看声明一个重入锁需要使用到那几个构造方法

    public ReentrantLock() {
            sync = new NonfairSync();
        }
     
    public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
    

    推荐声明方式

    private static ReentrantLock lock = new ReentrantLock(true);
    private static ReentrantLock locka = new ReentrantLock();
    

    重点说明:

    ReentrantLock提供了两个构造方法,对应两种声明方式。

    第一种声明的是公平锁,所谓公平锁,就?#21069;?#29031;时间先后顺序,使先等待的线程先得到锁,而且,公平锁不会产生饥饿锁,也就是只要排队等待,最终能等待?#20132;?#21462;锁的机会。

    第二种声明的是非公平锁,所谓非公平锁就和公平锁概念相反,线程等待的顺序并不一定是执行的顺序,也就是后来进来的线程可能先被执?#23567;?/p>

    ReentrantLock默认是非公平锁,因为:公平锁实现了先进先出的公平性,但是由于来一个线程就加入队列中,往往都需要阻塞,再由阻塞变为运行,这种上下文切换是非常好性能的。非公平锁由于允许插队所以,上下文切换少的多,性能比较好,保证的大的吞吐量,但是容易出现饥饿问题。所以?#23548;?#29983;产也是较多的使用非公平锁。

    非公平锁调用的是NonfairSync方法。

    二、加入锁之后lock方法到底是怎么处理的(只讲非公平锁)
    刚才我们说如果是非公平锁就调用NonfairSync方法,那我们就来看看这个方法都做来什?#30784;?br />

    static final class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;
     
            /**
             * Performs lock.  Try immediate barge, backing up to normal
             * acquire on failure.
             */
            final void lock() {
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    acquire(1);
            }
     
            protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
        }
    

    重点说明:

    读前先知:ReentrantLock用state表示“持有锁的线程已经重复获取该锁的次数”。当state(下文用状态二子代替)等于0时,表示当前没有线程持有锁)。
    第一步调用compareAndSetState方法,传了第一参数是期望值0,第二个参数是?#23548;?#20540;1,当前这个方法?#23548;?#26159;调用了unsafe.compareAndSwapInt实现CAS操作的,也就是上锁之前状态必须是0,如果是0调用setExclusiveOwnerThread方法

    private transient Thread exclusiveOwnerThread;
     
        
        protected final void setExclusiveOwnerThread(Thread thread) {
            exclusiveOwnerThread = thread;
        }
    
    

    可以看出setExclusiveOwnerThread就是线程设置为当前线程,此时说明有一名线程已经拿到了锁。大家都是CAS有三个值,如果旧值等于预期值,就把新值赋予上,所以当前线程得到了锁就会把状态置为1。

    第二步是compareAndSetState方法返回false时,此时调用的是acquire方法,参数传1

    tryAcquire()方法?#23548;?#26159;调用了nonfairTryAcquire()方法。

    public final void acquire(int arg) {
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }
     
    final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    if (compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0) // overflow
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }

    注释上说的很明白,请求独占锁,忽?#36816;?#26377;中断,至少执行一次tryAcquire,如果成功就返回,否则线程进入阻塞--唤醒两种状态切换中,直到tryAcquire成功。详情见链接tryAcquire()、addWaiter()、acquireQueued()挨个分析。

     好,到日前为止大家清楚了lock()方法到调用过程,清楚了,为什么只有得到锁的当前线程才可以执行,没有得到的会在队列里不停的利用CAS原理试图得到锁,CAS很高效,也就是,为什么ReentrantLock比synchronized高效的原因,缺点是很浪费cpu资源。

    三、所有线程都执行完毕后调用unlock()方法

    unlock()方法是通过AQS的release(int)方法实现的,我们可以看一下:

    public void unlock() {
            sync.release(1);
        }
     
     
    public final boolean release(int arg) {
            if (tryRelease(arg)) {
                Node h = head;
                if (h != null && h.waitStatus != 0)
                    unparkSuccessor(h);
                return true;
            }
            return false;
        }

    tryRelease()是由子类实现的,我们来看一下ReentrantLock中的Sync对它的实现:

    protected final boolean tryRelease(int releases) {
                int c = getState() - releases;
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                if (c == 0) {
                    free = true;
                    setExclusiveOwnerThread(null);
                }
                setState(c);
                return free;
            }

    先通过getState获得状态标识,如果这个标识和要释放的数量相等,就会把当前占有锁的线程设置为null,实现锁的释放,然后返回true,否则把状态标?#37117;?#21435;releases再返回false。

    以上所述是小编给大家介绍的java并发之重入锁-ReentrantLock详解整合,希望对大家有所帮助,如果大家有任?#25105;?#38382;请给我留言,小编会及时回复大家的。在此也非常?#34892;?#22823;家对脚本之家网站的支持!

    相关文章

    • maven的安装配置以及在IDEA中的配置图文教程

      maven的安装配置以及在IDEA中的配置图文教程

      下面小编就为大家分享一篇maven的安装配置以及在IDEA中的配置图文教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
      2017-12-12
    • JedisPool资源池优化方法

      JedisPool资源池优化方法

      这篇文章主要介绍了JedisPool资源池优化方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
      2018-03-03
    • Java中Finally关键字

      Java中Finally关键字

      与其他语言的模型相比,finally 关键字是对 Java 异常处理模型的最佳补充。接下来通过本文给大家介绍Java中Finally关键字及finally关键字的使用相关知识,?#34892;?#36259;的朋友一起学习吧
      2016-05-05
    • Java中final作用于变量、参数、方法及类该如何处理

      Java中final作用于变量、参数、方法及类该如何处理

      Java中的final关键字非常重要,它可以应用于类、方法以及变量,下面这篇文章主要给大家介绍了关于Java中final作用于变量、参数、方法及类该如何处理的相关资?#24076;?#25991;中通过示例代码介绍的非常详细,需要的朋友可以参考下。
      2017-12-12
    • Java实现DES加解密算法解析

      Java实现DES加解密算法解析

      这篇文章主要介绍了Java实现DES加解密算法解析,结合完整实例形式分析了DES?#29992;?#30340;相关原理,需要的朋友可以参考下。
      2016-10-10
    • Java实现表白小程序

      Java实现表白小程序

      本文讲述了Java实现表白的代码实例。具有很好的参考价值,希望对大家有所帮助,一起跟随小编过来看看吧,具体如下:
      2018-05-05
    • java多线程返回值使用示例(callable与futuretask)

      java多线程返回值使用示例(callable与futuretask)

      这篇文章主要介绍了多线程返回值使用示例(callable与futuretask),需要的朋友可以参考下
      2014-04-04
    • Java 判断IP地址的合法性实例详解

      Java 判断IP地址的合法性实例详解

      这篇文章主要介绍了Java 判断IP地址的合法性实例详解的相关资料,需要的朋友可以参考下
      2017-05-05
    • Java单例模式的讲解

      Java单例模式的讲解

      今天小编就为大家分享一篇关于Java单例模式的讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
      2019-01-01
    • ?#31243;窼pring Boot 异常处理篇

      ?#31243;窼pring Boot 异常处理篇

      本篇文章主要介绍了?#31243;窼pring Boot 异常处理篇,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
      2017-08-08

    最新评论

    山东群英会开奖查询