zoukankan      html  css  js  c++  java
  • JUC之Lock、ReentrantLock可重入独占锁

    前言

      ReentrantLock即可重入锁,实现了Lock和Serializable接口

      在java环境下ReentrantLock和Synchronized都是可重入锁

      ReentrantLock构造函数中提供两种锁:创建公平锁和非公平锁(默认)

    • ReentrantLock有三个内部类 Sync、NonfairSync和FairSync类。
    • Sync继承AbstractQueuedSynchronized抽象类
    • NonfairSync(非公平锁)继承Sync抽象类。
    • FairSync(公平锁)继承Sync抽象类。

      公平锁,线程按照发出的请求的顺序获取锁;非公平锁,允许插队。

      公平锁上,若有另外一个线程持有锁或有其他线程在等待队列中等待这个锁,那么新发出的请求的线程将被放入到对类中。

      非公平锁性能高于公平锁性能的原因

    • 在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟
    • 假设线程 A 持有一个锁,并且线程 B 请求这个锁。由于锁被 A 持有,因此 B 将被挂起。当 A 释放锁时,B 将被唤醒,因此 B 会再次尝试获取这个锁。与此同时,如果线程 C 也请求这个锁,那么 C 很可能会在 B 被完全唤醒之前获得、使用以及释放这个锁。这样就是一种双赢的局面,B 获得锁的时刻并没有推迟,C 更早的获得了锁,并且吞吐量提高了。

      当持有锁的时间相对较长或者请求锁的平均时间间隔较长,应该使用公平锁

    公平锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换。非公平锁虽然可能造成线程“饥饿”,但极少的线程切换,保证了其更大的吞吐量。

    源码

    ReentrantLock类是实现了Lock接口,先看看接口里的抽象方法

    Lock API

    void lock();  //获取锁
    void lockInterruptibly() throws InterruptedException;  //如果当前线程未被中断,则获取锁
    boolean tryLock();  //只有锁在空闲状态时才能获取该锁。
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;  //锁在给定时间内空闲,且当前线程未被中断,则获取锁
    void unlock();  //释放锁
    Condition newCondition();  //返回绑定到此Lock实例的新 Condition 实例。

     Lock接口中出现的Condition接口,它是什么作用呢?

    Condition

      wait()、notify()和synchronized配合可以实现等待通知,Condition和Lock配合同样也可以实现等待通知,但是两者之间还是有区别的

    Condition定义了等待/通知两种类型的方法,当前线程调用这些方法,需要提前获取到Condition对象关联的锁,Condition对象是由Lock对象创建出来的(Lock.newCondition),换句话说,Condition是依赖Lock对象的。

    ConditionAPI

    public interface Condition {
        void await() throws InterruptedException;  //当前线程进入等待状态直到被通知(signal)或被中断
        void awaitUninterruptibly();  //不响应中断等待,直到被通知(signal)
        long awaitNanos(long nanosTimeout) throws InterruptedException;  //等待指定时长直到被通知或中断或超时
        boolean await(long time, TimeUnit unit) throws InterruptedException;  //同上
        boolean awaitUntil(Date deadline) throws InterruptedException; //当前线程进入等待状态直到被通知、中断或者到某个事件。若没有到指定事件就被通知,方法返回true,否则false。
        void signal();  //唤醒一个等待在Condition上的线程,该线程从等待方法返回前必须获得与Condition相关联的锁。
        void signalAll();  //唤醒所有等待在Condition上的线程,该线程从等待方法返回前必须获得与Condition相关的锁
    }

    ReentrantLock源码

    构造器

    public ReentrantLock() {sync = new NonfairSync();}  //默认使用非公平锁对象
    public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}  //根据参数,判断使用 非公平/公平 锁。

    内部类

      构造器中,是创建内部类的对象NonfairSync(非公平锁)和FairSync(公平锁),它们都实现了Sync静态内部类

      Sync静态内部类源码

    abstract static class Sync extends AbstractQueuedSynchronizer {
            private static final long serialVersionUID = -5179523762034025860L;
            abstract void lock();
         //非公平锁获取锁 final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); //当前线程 int c = getState(); //同步状态 if (c == 0) { //如果为0,当前锁未被其他线程获取 if (compareAndSetState(0, acquires)) { //通过CAS设置 setExclusiveOwnerThread(current); //设置获取锁的此线程 return true; } } else if (current == getExclusiveOwnerThread()) { //如果锁状态不为0,则已经被其他线程获取。如果是当前线程,也就是同一个线程再次获取这个锁,即可重入 int nextc = c + acquires; //状态值增加。 if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); //设置状态值 return true; } return false; }      //释放锁 protected final boolean tryRelease(int releases) { int c = getState() - releases; //获取状态 if (Thread.currentThread() != getExclusiveOwnerThread()) //当前线程如果不是该锁的线程,抛异常 throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { //状态值为0,释放锁 free = true; setExclusiveOwnerThread(null); } setState(c); return free; }      //检查锁是否为此线程的所有者。就是之前是否是这个线程获取的锁 protected final boolean isHeldExclusively() { return getExclusiveOwnerThread() == Thread.currentThread(); }      //创建ConditionObject对象 final ConditionObject newCondition() { return new ConditionObject(); }      //返回该锁的线程 final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); }      //返回锁的状态值。如果是该锁的线程,返回对应的值;不是返回0 final int getHoldCount() { return isHeldExclusively() ? getState() : 0; }      //判断是否已被其他线程获取,是否已经锁住 final boolean isLocked() { return getState() != 0; } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); setState(0); // reset to unlocked state } }

    NonfairSync非公平锁的源码实现

      非公平锁就是一种获取锁的抢占机制,是 随机获得锁 的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了.

    static final class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;
         //执行锁 final void lock() { if (compareAndSetState(0, 1)) //通过CAS操作,设置状态值 setExclusiveOwnerThread(Thread.currentThread()); //设置该锁的线程 else acquire(1); }
         //尝试获取锁。默认是非公平锁,转至Sync中的方法
    protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }

    FairSync公平锁的源码实现

      公平锁表示线程获取锁顺序是按照 线程加锁的顺序 来分配的,即先来先得的 FIFO 先进先出顺序。

    static final class FairSync extends Sync {
            private static final long serialVersionUID = -3000897897090466540L;
         //执行加锁
    final void lock() { acquire(1); }
         //尝试获取锁 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); //当前线程 int c = getState(); //状态值 if (c == 0) { //等于0,该锁未被线程获取 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { //hasQueuedPredecessors()方法判断头节点是否为当前线程 setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { //如果不等于0,判断该锁的线程是否为当前线程,是可重入。 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }

    方法

    //获得锁
    public void lock() {
        sync.lock();
    }
    //当前线程未被中断,获取锁 public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } //该锁未被其他线程获取,则当前线程获取锁。默认非公平锁 public boolean tryLock() { return sync.nonfairTryAcquire(1); } //给定时间内,该锁未被其他线程获取,该线程未被中断,可获取锁 public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } //释放锁 public void unlock() { sync.release(1); } //返回Condition对象 public Condition newCondition() { return sync.newCondition(); } //返回同步状态 public int getHoldCount() { return sync.getHoldCount(); } //锁的持有者是否为当前线程 public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); } //该锁是否已被线程获取 public boolean isLocked() { return sync.isLocked(); } //判断是否为公平锁 public final boolean isFair() { return sync instanceof FairSync; } //返回拥有该锁的线程 protected Thread getOwner() { return sync.getOwner(); } //查询是否有线程正在等待获取此锁 public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); //这个方法就是判断 头节点==尾结点? 没有等待线程:有等待线程 } //判断线程是否在等待线程队列中 public final boolean hasQueuedThread(Thread thread) { return sync.isQueued(thread); } //返回等待线程数 public final int getQueueLength() { return sync.getQueueLength(); } //返回线程队列 protected Collection<Thread> getQueuedThreads() { return sync.getQueuedThreads(); } //查询是否有线程正在等待与此锁关联的给定条件。 public boolean hasWaiters(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition); } //返回与此锁关联的给定条件下等待的线程数的估计值。 public int getWaitQueueLength(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition); } //返回一个集合,其中包含可能正在等待与此锁关联的给定条件的线程。 protected Collection<Thread> getWaitingThreads(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition); }

    总结

    • ReentrantLock 提供了内置锁(synchronized)类似的功能和内存语义。
    • ReentrantLock 提供了包括定时的锁等待、可中断的锁等待、公平性以及实现非块结构的加锁。
    • Condition 对线程的等待和唤醒等操作更加灵活,一个 ReentrantLock 可以有多个 Condition 实例,更有扩展性。
    • ReentrantLock 需要显示的获取锁,并在 finally 中释放锁。
    • JDK 1.8 以前 ReentrantLock 在性能上似乎优于 synchronized,但获取锁的操作不能与特定的栈帧关联起来,而内置锁可以。
    • 因为内置锁时 JVM 的内置属性,所以未来更可能提升 synchronized 而不是 ReentrantLock 的性能。
      • 例如对线程封闭的锁对象消除优化,通过增加锁粒度来消除内置锁的同步。
     
  • 相关阅读:
    php用redis保存session
    php字符串常用算法--字符串加密解密
    PHP file_get_contents设置超时处理方法
    php栈数据结构和括号匹配算法
    JavaScript 函数参数传递到底是值传递还是引用传递
    JavaScript数据操作--原始值和引用值的操作本质
    JavaScript数据类型--值类型和引用类型
    js基本数据类型和typeof
    JS获取字符串实际长度(包含汉字)
    sublime的一些插件
  • 原文地址:https://www.cnblogs.com/FondWang/p/12112234.html
Copyright © 2011-2022 走看看