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 的性能。
    • 例如对线程封闭的锁对象消除优化,通过增加锁粒度来消除内置锁的同步。
 
posted @ 2019-12-30 13:29  王大军  阅读(290)  评论(0编辑  收藏  举报