zoukankan      html  css  js  c++  java
  • Java多线程系列---“JUC锁”06之 公平锁(下)

    转自:http://www.cnblogs.com/skywang12345/p/3496609.html

    释放公平锁(基于JDK1.7.0_40)

    1. unlock()

    unlock()在ReentrantLock.java中实现的,源码如下:

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

    说明
    unlock()是解锁函数,它是通过AQS的release()函数来实现的。
    在这里,“1”的含义和“获取锁的函数acquire(1)的含义”一样,它是设置“释放锁的状态”的参数。由于“公平锁”是可重入的,所以对于同一个线程,每释放锁一次,锁的状态-1。

    关于AQS, ReentrantLock 和 sync的关系如下:

    复制代码
    public class ReentrantLock implements Lock, java.io.Serializable {
    
        private final Sync sync;
    
        abstract static class Sync extends AbstractQueuedSynchronizer {
            ...
        }
    
        ...
    }
    复制代码

    从中,我们发现:sync是ReentrantLock.java中的成员对象,而Sync是AQS的子类。

    2. release()

    release()在AQS中实现的,源码如下:

    复制代码
    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()会先调用tryRelease()来尝试释放当前线程锁持有的锁。成功的话,则唤醒后继等待线程,并返回true。否则,直接返回false。

    3. tryRelease()

    tryRelease()在ReentrantLock.java的Sync类中实现,源码如下:

    复制代码
    protected final boolean tryRelease(int releases) {
        // c是本次释放锁之后的状态
        int c = getState() - releases;
        // 如果“当前线程”不是“锁的持有者”,则抛出异常!
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
    
        boolean free = false;
        // 如果“锁”已经被当前线程彻底释放,则设置“锁”的持有者为null,即锁是可获取状态。
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        // 设置当前线程的锁的状态。
        setState(c);
        return free;
    }
    复制代码

    说明
    tryRelease()的作用是尝试释放锁。
    (01) 如果“当前线程”不是“锁的持有者”,则抛出异常。
    (02) 如果“当前线程”在本次释放锁操作之后,对锁的拥有状态是0(即,当前线程彻底释放该“锁”),则设置“锁”的持有者为null,即锁是可获取状态。同时,更新当前线程的锁的状态为0。
    getState(), setState()在前一章已经介绍过,这里不再说明。
    getExclusiveOwnerThread(), setExclusiveOwnerThread()在AQS的父类AbstractOwnableSynchronizer.java中定义,源码如下:

    复制代码
    public abstract class AbstractOwnableSynchronizer
        implements java.io.Serializable {
    
        // “锁”的持有线程
        private transient Thread exclusiveOwnerThread;
    
        // 设置“锁的持有线程”为t
        protected final void setExclusiveOwnerThread(Thread t) {
            exclusiveOwnerThread = t;
        }
    
        // 获取“锁的持有线程”
        protected final Thread getExclusiveOwnerThread() {
            return exclusiveOwnerThread;
        }
       
        ...
    }
    复制代码

    4. unparkSuccessor()

    在release()中“当前线程”释放锁成功的话,会唤醒当前线程的后继线程。
    根据CLH队列的FIFO规则,“当前线程”(即已经获取锁的线程)肯定是head;如果CLH队列非空的话,则唤醒锁的下一个等待线程。
    下面看看unparkSuccessor()的源码,它在AQS中实现。

    复制代码
    private void unparkSuccessor(Node node) {
        // 获取当前线程的状态
        int ws = node.waitStatus;
        // 如果状态<0,则设置状态=0
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
    
        //获取当前节点的“有效的后继节点”,无效的话,则通过for循环进行获取。
        // 这里的有效,是指“后继节点对应的线程状态<=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);
    }
    复制代码

    说明
    unparkSuccessor()的作用是“唤醒当前线程的后继线程”。后继线程被唤醒之后,就可以获取该锁并恢复运行了。
    关于node.waitStatus的说明,请参考“上一章关于Node类的介绍”。

    总结

    “释放锁”的过程相对“获取锁”的过程比较简单。释放锁时,主要进行的操作,是更新当前线程对应的锁的状态。如果当前线程对锁已经彻底释放,则设置“锁”的持有线程为null,设置当前线程的状态为空,然后唤醒后继线程。

  • 相关阅读:
    进制
    流程控制
    运算符
    格式化输出
    数据结构-树的遍历
    A1004 Counting Leaves (30分)
    A1106 Lowest Price in Supply Chain (25分)
    A1094 The Largest Generation (25分)
    A1090 Highest Price in Supply Chain (25分)
    A1079 Total Sales of Supply Chain (25分)
  • 原文地址:https://www.cnblogs.com/Hermioner/p/9940625.html
Copyright © 2011-2022 走看看