ReentrantLock

209 阅读4分钟

ReentrantLock-

前言: 为什么叫可重入锁,简单来说就是一个线程可以重复获取锁,那么这种又是怎么实现的可重入锁,本质上来说是依赖AQS为基础来实现的,在AQS类里面有一个内部类Node 就是一个链表,里面存了 上一个下一个 第一 最后一个 这种节点对象,每个节点对象里面,都有一个Thread 表示他是哪个线程的Node 如果节点是head 那么就获取,那么这个过程其实就算是 伪过程了,下面的内容讲讲,介绍一下,可重入锁 这个类;

内部类Sync:

提供所有实现机制的同步器,两个类继承了该类,一个是同步锁,一个是非同步锁。

abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;
​
    /**
      主要是为非公平锁,提供快捷方法
    **/
    abstract void lock();
​
    //非公平的方法获取锁,
    @ReservedStackAccess
    final boolean nonfairTryAcquire(int acquires) {
      //获取当前线程
        final Thread current = Thread.currentThread();
      //获取当前锁的状态 
        int c = getState();
      //判断当前锁,是否无人暂用 0无人 1有一个锁 2有两个锁
        if (c == 0) {
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        //判断当前锁是否是独有线程-如果是进入
        else if (current == getExclusiveOwnerThread()) {
            //当前的state + 1 基本上都是
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            //设置status 会有节点,AQS有一个遍历节点来执行的
            setState(nextc);
            return true;
        }
        //有人占用锁,且节点里面的线程并不是自有线程 
        return false;
    }
  
  //尝试发布信息 也就是修改status 状态 
  //这个方法,其实就是将status-1 在这个类里面有一个解锁的方法,就是调用这个方法 然后传入1 
  @ReservedStackAccess
  protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
    //初始化状态
            boolean free = false;
            if (c == 0) {
              //如果等于0 status 就证明没人用,这里状态就为tree 
              //这里体现了抽象类的文档规则,如果是free 就为true否则为false
                free = true;
              //设置线程持有该锁
                setExclusiveOwnerThread(null);
            }
             //更新状态
            setState(c);
            return free;
        }
        
        //判断持有锁的线程是否是当前线程 
        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
        
      //创建一个新的条件
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
​
    
        //获取持有线程
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
        //获取计数
        final int getHoldCount() {
          //如果是当前线程持有-那么获取status 否则直接返回0
            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); //重制当前锁
        }
}
  

内部类非公平对象NonfairSync:

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;
    @ReservedStackAccess
    final void lock() {
      //这里是一个cas方法,判断当前是否无锁-如果无锁设置为1
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            //如果有锁就+1 
            acquire(1);
    }
​
    protected final boolean tryAcquire(int acquires) {
      //父类的非公平锁-实现
        return nonfairTryAcquire(acquires);
    }
}
​
//这个方法是AQS类里面的方法 
@ReservedStackAccess
public final void acquire(int arg) {
  //调用子类的方法 -&&获取排队(其实就是添加一个节点)
  如果成功-中断
        if (!tryAcquire(arg) &&
            //尝试获取-知道持有该线程的节点=head
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            //中断
            selfInterrupt();
    }

内部类公平锁:FairSync

其实公平锁要比非公平锁,来讲效率低一些,应为如果要是有人在排队,那么就直接 排队去了 所有人都在排队,所有人都在抢,抢到了中断,期间一直都是cas的过程 比较交换-效率自然比较慢了

/**
 * Sync object for fair locks
 */
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;
​
    final void lock() {
      //直接调用
        acquire(1);
    }
​
    @ReservedStackAccess
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        //获取当前状态
        int c = getState();
        if (c == 0) {
            //查看是否有前辈队列,如果没有才可以像非公平锁一样获取
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
      //有前辈队列
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            //将自己 添加进入队列
            setState(nextc);
            return true;
        }
        return false;
    }
}

注意:

1:因为其默认创建就是非公平对象,如果想要使用公平锁-那么就在创建ReentrantLock(true) 传入boolean值 true 为公平锁 fasle为非公平锁

2:lockInterruptibly

允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException但是正常lock 不会抛出异常,只是设置一个中断状态其就是将中断状态设置为true

那么什么是中断,其实就是设置一个中断状态,如果调用到该线程的时候,是中断状态,没有被唤醒,那么将会被挂起park 如果这个方法被中断,那么其结果就是抛出异常InterruptedException

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

3:tryLock 这个其实就是如果被中断,那么就在多长时间内将会被唤醒,唤醒后继续进行循环调用

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

锁的获取过程

未命名文件.jpg