您当前的位置: 首页 > 

恐龙弟旺仔

暂无认证

  • 0浏览

    0关注

    282博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

JDK源码解析之AbstractQueuedSynchronizer

恐龙弟旺仔 发布时间:2019-01-15 17:56:58 ,浏览量:0

前言:

    JDK中的锁大致可以分为两类:Synchronized和CAS。

    CAS的使用中,有直接使用的,比如AtomicInteger;有间接使用的比如ReentrantLock。关于AtomicInteger的分析可参考笔者的上一篇博客:

    CAS间接实现的典型代表是ReentrantLock和ReentrantReadWriteLock。

    本文先不具体介绍ReentrantLock的实现方式,而且先介绍一下其基础类AbstractQueuedSynchronizer类,该类作为CAS的抽象类实现类,是并发包(java.util.concurrent)下重要的基础类。

 

1.AbstractQueuedSynchronizer(AQS)使用范例

    AQS是一个用来构建锁和同步器的框架。

    以下范例来自于网络(有删减):https://www.cnblogs.com/chengxiao/p/7141160.html 

 

    1)创建Mutex类用于实现AQS
public class Mutex implements java.io.Serializable {
    
    //同步对象完成一系列复杂的操作,我们仅需指向它即可
    private final Sync sync = new Sync();
    //加锁操作,代理到acquire(模板方法)上就行,acquire会调用我们重写的tryAcquire方法
    public void lock() {
        sync.acquire(1);
    }
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }
    //释放锁,代理到release(模板方法)上就行,release会调用我们重写的tryRelease方法。
    public void unlock() {
        sync.release(1);
    }
    public boolean isLocked() {
        return sync.isHeldExclusively();
    }
    
    /** 注意这个静态内部类 */
    //静态内部类,继承AQS
    private static class Sync extends AbstractQueuedSynchronizer {
        //是否处于占用状态
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }
        //当状态为0的时候获取锁,CAS操作成功,则state状态为1,
        public boolean tryAcquire(int acquires) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        //释放锁,将同步状态置为0
        protected boolean tryRelease(int releases) {
            if (getState() == 0) throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
    }
}

    总结:本例中使用静态内部值Sync实现AQS,主要实现其tryAcquire()和tryRelease()方法

 

    2)测试类
public class TestMutex {
    private static CyclicBarrier barrier = new CyclicBarrier(31);
    private static int a = 0;
    private static  Mutex mutex = new Mutex();

    public static void main(String []args) throws Exception {
        //说明:我们启用30个线程,每个线程对i自加10000次,同步正常的话,最终结果应为300000;
        
        //加锁后
        barrier.reset();//重置CyclicBarrier
        a=0;
        for(int i=0;i0,说明是CANCELLED状态,则说明前节点已经无效,则从后向前遍历,找到一个非CANCELLED状态
        // 的节点,并将当前节点设置为其后置节点
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        // 其他情况(CONDITION、PROPAGATE)这两种状态的前节点,将前节点设置为SIGNAL状态
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

// 检查线程是否被中断过,如果被设置过则返回true
private final boolean parkAndCheckInterrupt() {
    // 当前线程进入wait
    LockSupport.park(this);
    return Thread.interrupted();
}

    总结:加锁过程可总结为以下三步

    * 首先通过tryAcquire()方法来获取同步状态,成功则返回,失败进行下一步骤操作

    * 创建Node节点,加入同步队列中

    * 加入队列的Node节点,如果其前节点是head,则再次调用tryAcquire()方法来获取同步状态。否则当其前节点的状态为SIGNAL时,线程便进入阻塞状态,直至被中断或唤醒

 

    用图再来说明一下整个过程,图片来自(http://www.cnblogs.com/waterystone/p/4920797.html ):

    做一个比喻来说:

    我们去医院排队挂号,等医生看病,这个资源就是医生本身,医生每一次只能给一位挂号的病人看病,目前在看病的也就是head节点。

    1)又来了一位病人

        他去尝试获取state,也就是让医生帮看病,发现资源被人占用(因为head在医生那正看着呢),所以他就去排队,排到队尾,也就是tail节点,然后就去休息(执行了LockSupport.park(this)方法)

    2)第一个病人看好了病之后

        他去释放锁,也就是从医生那出来,(喊了一嗓子,大家可以去医生那看病啦,我看好了,执行LockSupport.unpark方法),然后休息的线程都醒来了,去竞争资源,但是只有等待线程2竞争资源成功,那么他接着进去看病,他就被设置为head

 

 

3.Mutex.unlock()释放锁
public void unlock() {
    sync.release(1);
}

// AbstractQueuedSynchronizer.release()
public final boolean release(int arg) {
    // tryRelease方法调用的是Mutex.tryRelease()
    if (tryRelease(arg)) {
        // 获取头结点,如果头结点状态不为0,目前是SIGNAL=-1
        Node h = head;
        if (h != null && h.waitStatus != 0)
            // 则执行unparkSuccessor方法
            unparkSuccessor(h);
        return true;
    }
    return false;
}
    1) AbstractQueuedSynchronizer.unparkSuccessor()
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    // 如果head的状态为SIGNAL,则设置为0
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    Node s = node.next;
    // 非正常情况下,head的next结点状态为CANCELLED,则从tail结点向前寻找,
    // 找到一个正常结点后将其设置为head的next结点
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus             
关注
打赏
1655041699
查看更多评论
0.0468s