Lock底层原理详解

1,532 阅读3分钟

前言

J.U.C

正文

Lock

Lock的出现可以解决Synchronized在某些场景中的短板,它比Synchronized更加灵活。

结构图

public interface Lock {
    void lock();
    
    void lockInterruptibly() throws InterruptedException;
    
    boolean tryLock();

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    
    void unlock();
    
    Condition newCondition();
}

Lock是一个接口,其有多个实现,我们经常使用的是ReentrantLock,这里我们拿其举例说明,其他类似。

ReentrantLock有个内部抽象类Sync其继承了AbstractQueuedSynchronizer(AQS)这是其锁逻辑实现的核心,其有个被volatile修饰的state字段记录锁重入的次数,AQS的父类AbstractOwnableSynchronizer(AOS)记录被保存的线程。

Sync有两个具体的实现FairSync(公平锁),NonfairSync(非公平锁)。

ReentrantLock

锁都是可以重入的,我还没见过不可以重入的锁。

static Lock lock=new ReentrantLock(true);
 
    public static void inc(){
        lock.lock(); //获得锁(互斥锁) ThreadA 获得了锁
        try {
            Thread.sleep(1);
            count++;
            decr();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();//释放锁 ThreadA释放锁  state=1-1=0
        }
    }
    public static void decr(){
        lock.lock(); #state=2   //ThreadA再次来抢占锁 : 不需要再次抢占锁,而是只增加重入的次数
        try{
            count--;
        }finally {
            lock.unlock(); //state=1
        }
    }

lock() 加锁

public class ReentrantLock implements Lock, java.io.Serializable {
    public ReentrantLock() {
        sync = new NonfairSync();
    }

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

    public void lock() {
        sync.lock();
    }
}

根据lock方法,找到内部类Sync的lock()

abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        abstract void lock();
}

根据ReentrantLock的构造方法可知,默认实现的是NonfairSync,我们直接进入对应实现的lock方法

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        final void lock() {
            if (compareAndSetState(0, 1))   //乐观锁
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    }

这里是通过CAS乐观锁compareAndSetState去判断是否锁是否已被持有。

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
	/**
     * The synchronization state.
     */
  private volatile int state;

  protected final boolean compareAndSetState(int expect, int update) {
      // See below for intrinsics setup to support this
      return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
  }
}

由此可知通过被volatile修饰的state内存偏移量为0时为锁未被持有,然后设置当前线程为独占线程。

public abstract class AbstractOwnableSynchronizer implements java.io.Serializable {

    private transient Thread exclusiveOwnerThread;

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

如果没有抢占到锁呢

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    final void lock() {
        if (compareAndSetState(0, 1))   //乐观锁 这里保证只有一个线程进入
            setExclusiveOwnerThread(Thread.currentThread());  //设置独占线程
        else
            acquire(1);
    }
}

进入acquire(1)方法

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
}

这里有三个方法

  1. !tryAcquire(arg) :确认锁的状态,尝试去获取锁
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
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; //state累加,增加重入次数
          if (nextc < 0) // overflow
              throw new Error("Maximum lock count exceeded");
          setState(nextc);  
          return true;
      }
      return false;
}

这里如果直接成功,就直接返回。

  1. addWaiter(Node.EXCLUSIVE) 将当前线程(未抢占到锁)存入双向链表(等待队列)中
private Node addWaiter(Node mode) {
      Node node = new Node(Thread.currentThread(), mode);
      // Try the fast path of enq; backup to full enq on failure
      Node pred = tail;
      if (pred != null) {
          node.prev = pred;
          if (compareAndSetTail(pred, node)) {
              pred.next = node;
              return node;
          }
      }
      enq(node);
      return node;
}
    
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
 }
  1. acquireQueued 抢占锁,或者阻塞
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();   //这里拿到当前节点的前一个节点
                if (p == head && tryAcquire(arg)) {   /如果前节点是头节点,这里会触发一次头节点抢占锁
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) && //这里根据其前一个节点的状态判断是否需要挂起当前线程
                    parkAndCheckInterrupt())  
                    interrupted = true;              //返回加入队列成功
            }
        } finally {
            if (failed)
                cancelAcquire(node); 
        }
}

//这里是校验并改变前节点的状态
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
    if (ws > 0) {   //把取消状态的节点移除
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {   //
        
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}


private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

unLock() 释放锁

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;
}
private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

将锁的重入次数-1,在链表中移除当前节点,并unPark当前节点的next节点的线程,然后这个释放的节点就会进行自旋去抢占锁。

总结

  1. lock的存储结构:一个int类型状态值(用于锁的状态变更),一个双向链表(用于存储等待中的线程)
  2. lock获取锁的过程:本质上是通过CAS来获取状态值修改,如果当场没获取到,会将该线程放在线程等待链表中。
  3. lock释放锁的过程:修改状态值,调整等待链表。