lock

lock 中的方法:

  

 

 

 

AQS:   AbstractQueuedSynchronizer   抽象队列同步器      ( juc中最复杂的一个类?? 它做了什么事情?)

  主要就是支持两个操作:  获取锁 和 释放锁 。

   获取锁: 首先判断状态是否允许获取锁, 如果允许获取锁,则获取锁,并修改状态; 如果获取锁失败,则进入到阻塞队列。

   释放锁: 修改状态,阻塞队列中的阻塞线程尝试获取锁。

  要实现 获取和释放锁,需要有这几个东西:

    状态位    (用来判断能不能获取锁)

    阻塞队列 (阻塞线程放到这里面)

         阻塞和唤醒线程

  具体的实现:(在AQS中)

    有一个 int 类型的 state 变量, 并且用volatile进行了修饰。

        如何阻塞(挂起)和唤醒线程? 可以用wait和notify但是不合适?为什么?   采用的 LockSupport 类中的 park 和 unpark等方法来实现的阻塞和唤醒线程。

     采用 CHL来实现阻塞队列。 完成入队和出队的过程。队列中的元素都是 Node 类型。

 

ReentrantLock:

  lock()方法的实现

   1) 调用syn的lock()方法

     公平锁:

     final void lock() {
            acquire(1);
        }

    2) AQS 中的 acquire 方法  (先尝试获取锁,如果获取失败,则入队到阻塞队列中)

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&    // 尝试获取锁   如果获取到  则结束了  if中的条件不满足
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))    //  如果当前线程的中断状态为true, 则执行 selfInterrupt方法,否则不执行
            selfInterrupt();   // 将 自己线程的中断状态设置为 true; 有什么用呢? 不清楚!!
    }

  3)  ReentrantLock中的 FairSync 中重写了 tryAcquire 方法   (尝试获取锁)

    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);  // 修改同步状态    可重入锁,进入一次,同步状态+1
                return true;
            }
            return false;
        }
    }

  AQS中的 addWaiter方法。 

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;
    }

  AQS中的 enq 方法。

  private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize         // 如果尾节点为null
                if (compareAndSetHead(new Node()))     // 设置头节点  可见 头节点是一个尾节点,头节点的下一个结点才是正式的头节点
                    tail = head;
            } else {
                node.prev = t;            // 当前结点作为尾结点   入队!
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

  AQS 中的 acquireQueued 方法。   返回的是当前线程的中断状态,如果执行了该线程了 interrupt方法,那么中断状态就是true,就会返回true,否则,返回false.

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true; 
        try {
            boolean interrupted = false;
            for (;;) {    // 死循环   
                final Node p = node.predecessor();      // 获取 node的前一个结点
                if (p == head && tryAcquire(arg)) {   / /如果当前结点的前一个结点是 头节点,并且 tryAcquire 方法尝试获取锁,成功获取到
                    setHead(node);                   // 设置当前结点为头节点 
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;     // 返回false,则 acquire中的 if中的判断语句为假,不执行 selfInterrupt()方法,
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

  AQS的中的Node类中的predecessor方法, 来获取的node的前一个结点。

      final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

  AQS 的 shouldParkAfterFailedAcquire 方法

  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) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);   // 设置上一个结点的 waitState状态为 SIGNAL, 这样才能唤醒下一个结点
        }
        return false;
    }

AQS 中的 parkAndCheckInterrupt 方法

   private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();   // 返回当前线程的中断状态
    }

LockSupport中的park 方法。

   public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);   //挂起的当前线程??
        setBlocker(t, null);
    }

 

unlock方法的实现:

    public void unlock() {
        sync.release(1);
    }

   1)调用AQS中的 release 方法

  public final boolean release(int arg) {
        if (tryRelease(arg)) {    //  如果当前没有线程占有该锁
            Node h = head;
            if (h != null && h.waitStatus != 0)  // 保证阻塞队列中还有被挂起的线程
                unparkSuccessor(h);  // h是头结点
            return true;
        }
        return false;
    }

  2)Sync中重写了 tryRelease 方法

    protected final boolean tryRelease(int releases) { // 如果没有线程占有锁,则返回true, 否则,返回 false.
            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;
        }

  3) AQS  中的 unparkSuccessor 方法

private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        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);    // 唤醒线程  还是通过 LockSupport 类中的方法,不过umpark方法需要传入要被唤醒的线程
}

非公平锁的 lock实现:

final void lock() {
            if (compareAndSetState(0, 1))           // 多了这个直接获取锁过程
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

 

tryLock 方法的实现:

   public boolean tryLock() {
        return sync.nonfairTryAcquire(1);   // 获取到返回true, 没获取到返回false
    }

  1)Sync中的 nonfairTryAcquire 方法

   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;
        }

带时间的 tryLock 方法的实现:

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

  1)调用AQS中的   tryAcquireNanos  方法

   public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())     // 如果当前线程的中断状态为true, 则抛出异常。
            throw new InterruptedException();
        return tryAcquire(arg) ||           // 先尝试获取一次锁,如果成功,直接返回true
            doAcquireNanos(arg, nanosTimeout);   // 
    }

  2)调用AQS中的  doAcquireNanos  方法

private boolean doAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;   //  通过系统的当前时间+等待时间  得到截至时间
        final Node node = addWaiter(Node.EXCLUSIVE);  // 将当前线程 入队, 添加到阻塞队列
        boolean failed = true;
        try {
            for (;;) {   // 死循环
                final Node p = node.predecessor();   // 获取当前结点的前一个结点
                if (p == head && tryAcquire(arg)) {   // 如果当前结点的前一个结点是头节点,并且获取锁成功
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return true;
                }
                nanosTimeout = deadline - System.nanoTime(); // 剩余时间
                if (nanosTimeout <= 0L)  // 如果剩余时间小于0 ,返回false;
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);  // 时间到了,被唤醒后,还有可能会获取到锁,因为for循环中,获取锁的语句排在前面
                if (Thread.interrupted())  // 如果线程的中断状态为true, 抛出异常
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

  3)调用 LockSupport 中的 parkNanos 方法来 阻塞(挂起)当前线程

  public static void parkNanos(Object blocker, long nanos) {
        if (nanos > 0) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            UNSAFE.park(false, nanos);
            setBlocker(t, null);
        }
    }

lockInterruptibly方法的实现

  与lock不同的地方就在于,这个方法会抛出异常,如果线程的中断状态为true的话。

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

  1)  AQS中的 acquireInterruptibly方法

    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())    // 如果当前线程被中断,抛出异常
            throw new InterruptedException();
        if (!tryAcquire(arg))          // 如果成功获取到锁,则直接返回; 否则,调用下面的方法
            doAcquireInterruptibly(arg); 
    }

  2)AQS中的 doAcquireInterruptibly 方法

 private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);   // 入队
        boolean failed = true;
        try {
            for (;;) { // 死循环
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())    // 如果当前线程的中断状态为true,则抛出异常。
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

 

LockSupport 类:

  park 和 unpark 怎么实现的 阻塞(挂起) 和 唤醒 线程。

LockSupport 中的 park 和 unpark 方法,底层都是通过 UNSAFE类来实现的。

  park   方法:

   public static void park() {
        UNSAFE.park(false, 0L);
    }

  unpark 方法:

   public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

 

中断线程 : interrupt 

获取到线程中断状态为true后,就抛出异常的目的是什么?

 

posted @ 2020-11-23 20:11  你眼里的星辰  阅读(273)  评论(0)    收藏  举报