Java并发编程--AQS的锁获取和释放

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_34168157/article/details/80967518
   /**
     * acquire(int arg):以独占模式获取对象,忽略中断。

     acquireInterruptibly(int arg): 以独占模式获取对象,如果被中断则中止。

     acquireShared(int arg): 以共享模式获取对象,忽略中断。

     acquireSharedInterruptibly(int arg)以共享模式获取对象,如果被中断则中止。

     tryAcquire(int arg):试图在独占模式下获取对象状态。

     tryAcquireNanos(int arg, long nanosTimeout):试图以独占模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败。

     tryAcquireShared(int arg):试图在共享模式下获取对象状态。

     tryAcquireSharedNanos(int arg, long nanosTimeout):试图以共享模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败。

     tryAcquire:去尝试获取锁,获取成功则设置锁状态并返回true,否则返回false。

     addWaiter:将当前线程加入到CLH队列队尾。

     acquireQueued:当前线程会根据公平性原则来进行阻塞等待,直到获取锁为止;并且返回当前线程在等待过程中有没有中断过。

     selfInterrupt:产生一个中断。
     */
    //请求获取锁,直接使用独占方式来占有锁,不会发生中断
    public final void acquire(int arg) {
        if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

    //试图在独占模式下获取锁,具体的异常实现需要在子类中自己进行实现
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

    /**
     * 当前线程会根据公平性原则来进行阻塞等待,而且在返回当前线程之前没有发生中断
     * @param node
     * @param arg
     * @return
     */
    final boolean acquireQueued(final Node node, int arg) {
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GCreturn interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                    interrupted = true;
            }
        } catch (RuntimeException ex) {
            cancelAcquire(node);
            throw ex;
        }

        /**
         * 仅仅直接返回一个异常, 具体的异常信息返回之后根据不同异常信息进行再次封装
         */
        protected boolean tryAcquire(int arg) {
            throw new UnsupportedOperationException();
        }

    /**
     *尝试获取锁,并判断是否需要挂起,并返回
     * @param node
     * @param arg
     * @return
     */
    final boolean acquireQueued(final Node node, int arg) {
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GCreturn interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                    interrupted = true;
            }
        } catch (RuntimeException ex) {
            cancelAcquire(node);
            throw ex;
        }
    }

    /**
     * 如果在尝试获取锁过程中发生中断,需要调用该中断方法
     */
    private static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }

    /**
     * AQS释放锁的方法主要有:

     release(int arg):以独占模式释放对象。

     releaseShared(int arg): 以共享模式释放对象

     tryRelease(int arg):试图设置状态来反映独占模式下的一个释放。

     tryReleaseShared(int arg):试图设置状态来反映共享模式下的一个释放。
     */
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

    //AQS也同样没有提供实现,具体实现方法要其子类自己内部实现,AQS仅仅只是抛出一个异常。
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }
//当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起;在释放锁后,
需要唤醒该线程的继任节点 lock方法,在调用acquireQueued():
    if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()){
        interrupted = true;
    }

    //在acquireQueued()中调用parkAndCheckInterrupt()来挂起当前线程:

    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
//调用LockSupport.park()方法。对于park():为了线程调度,在许可可用之前禁用当前线程。
//释放锁后,需要唤醒该线程继任节点:
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
//在release方法中调用unparkSuccessor()来唤醒该线程的继任节点。
在unparkSuccessor()方法中通过LockSupport.unpark()来唤醒。
unpark():如果给定线程的许可尚不可用,则使其可用。
//LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。每个使用LockSupport的线程都会与一个许可关联,
如果该许可可用,并且可在进程中使用,则调用park()将会立即返回,否则可能阻塞。如果许可尚不可用,
则可以调用 unpark 使其可用。但是注意许可不可重入,也就是说只能调用一次park()方法,否则会一直阻塞。
//LockSupport.park()、LockSupport.unpark()的作用分别是阻塞线程和解除阻塞线程,且park()和unpark()
不会遇到“Thread.suspend ()和 Thread.resume所可能引发的死锁”问题。当然park()、unpark()是成对使用。
//park():如果许可可用,则使用该许可,并且该调用立即返回;否则,为线程调度禁用当前线程,并在发生以下三种情况之一以前,使其处于休眠状态:
//其他某个线程将当前线程作为目标调用 unpark;或者其他某个线程中断当前线程;或者该调用不合逻辑地(即毫无理由地)返回。
// 一般来说park()、unpark()是成对出现的,同时unpark必须要在park执行之后执行,
当然并不是说没有不调用unpark线程就会一直阻塞,park有一个方法,
它带了时间戳(parkNanos(long nanos):为了线程调度禁用当前线程,
最多等待指定的等待时间,除非许可可用。)
    //其源码实现如下:
    public static void park() {
        unsafe.park(false, 0L);
    }
// unpark:如果给定线程的许可尚不可用,则使其可用。如果线程在 park 上受阻塞,则它将解除其阻塞状态。否则,保证下一次调用 park 不会受阻塞。如果给定线程尚未启动,则无法保证此操作有任何效果。
    //其源代码如下:
    public static void unpark(Thread thread) {
        if (thread != null) {
            Object lock = unsafe.getObject(thread, lockOffset);
            synchronized (lock) {
                if (thread.isAlive()) {
                    unsafe.unpark(thread);
                }
            }
        }
    }

猜你喜欢

转载自blog.csdn.net/baidu_34168157/article/details/80967518
今日推荐