JUC源码分析7-locks-AQS-共享模式

原创 2016年05月06日 17:42:17

AQS中一定要记住2点:

1.处理流程:

if(!请求成功)

加入队列

2.请求是对state的判断,AQS不关心你state表示什么,你可以表示状态也可以表示数量,由子类实现对请求的判断。将规则的判断和规则的处理分离,有点像模板模式。

先想想什么是独占什么是共享,举个栗子:独占就像大家拿号去排队体检,你拿号了发现前面还有n个人,没办法,等吧,然后你前面的人体检完了,医生就说,你通知下一位吧,ok,你出来通知排你后面的人,这个人有可能是跟占座位似得就放在纸在哪,所以你跳过他,再通知后面真正有人的进去。而共享则不同了,这个号可能不止属于你一个人,可能属于你公司所有体检的人,所以拿号排队轮到你的时候,你就需要通知排队的所有同事,大家一起体检去啊(这个共享的不太恰当,应该当成condition例子来说才好)。感觉独占和共享的大概意思就是这样。

还是看下AQS的共享模式

public final void acquireShared(int arg) {
//tryAcquireShared请求判断又是由子类实现判断
    if (tryAcquireShared(arg) < 0)
    //失败后加入队列
        doAcquireShared(arg);
}

private void doAcquireShared(int arg) {
//节点状态是共享,之前的独占模式是EXCLUSIVE
//跟独占一样加入队列
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
        //还是判断pre是不是头结点,是就再次请求
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                //这里和独占不同,独占模式下只是设置成头结点
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

发现共享整个挂起的doAcquireShared方法跟独占模式的acquireQueued处理差不多,唯一有区别的似乎就是如果pre节点是头结点,当前节点请求成功,独占模式只是将节点设置为head然后return,这里除了sethead还将唤醒队列的中其他节点,其他方法不管,继续看setHeadAndPropagate:

private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // Record old head for check below
    setHead(node);

    if (propagate > 0 || h == null || h.waitStatus < 0) {
        Node s = node.next;
        //当前node请求成功后判断next节点是共享节点就继续release
        if (s == null || s.isShared())
            doReleaseShared();
    }
}
private void doReleaseShared() {
    /*
			使用for保证队列中节点一定会被传递,即使有其他acquire或release在进行
     */
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            //节点状态为SIGNAL表示需要向后传递
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // 失败了就loop
                unparkSuccessor(h);
            }
            //为0就设置成PROPAGETE表示需要传播
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}

这里最重要的是理解doReleaseShared,一个进程doReleaseShared,然后unparkSuccessor,这时候被唤醒的其他线程可能线程切换运行,重新请求修改head节点,没机会的话,这里检查head节点没有变化就继续for,一直等到被唤醒的线程时间片切换到,然后再将继续修改下一个节点,到最后队列中的所有节点都被唤醒。

这里一定要想着线程切换多看看几遍,我当时就郁闷了半天这个疑问。

对应的acquireSharedInterruptibly响应中断和tryAcquireSharedNanos响应中断超时,跟独占的都差不多。


共享模式release,这个没什么好说的,释放判断成功就doReleaseShared,把队列中所有节点release

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}


共享跟独占的流程图差不多,不想画了,改天学习下AQS的condition。

参考:

http://www.infoq.com/cn/articles/java8-abstractqueuedsynchronizer

JAVA并发编程学习笔记之AQS源码分析(共享与互斥)

共享模式与独占模式 AQL的内部队列采用的是CLH队列锁模型,CLH队列是由一个一个结点(Node)构成的。Node类中有两个常量SHARE和EXCLUSIVE,顾名思义这两个常量用于表示这个结点支...
  • aesop_wubo
  • aesop_wubo
  • 2012年05月15日 22:39
  • 4510

java并发-独占锁与共享锁

1 锁的独占与共享       java并发包提供的加锁模式分为独占锁和共享锁,独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock就是以独占方式实现的互斥锁。共享锁,则允许多个线程同...
  • wojiushiwo945you
  • wojiushiwo945you
  • 2014年12月31日 11:34
  • 9441

JUC源码分析7-locks-AQS-共享模式

AQS中一定要记住2点: 1.处理流程: if(!请求成功) 加入队列 2.请求是对state的判断,AQS不关心你state表示什么,你可以表示状态也可以表示数量,由子类实现对请求的判断。将...
  • xiaoxufox
  • xiaoxufox
  • 2016年05月06日 17:42
  • 1228

共享式AQS

共享式AQS 阻塞式锁,在同一时刻只能有一个线程在执行,当一个线程执行完成后,再去释放下一个线程,而共享式是指,锁是可以被共享的,表现形式为,在同一时刻可以有多个线程运行。 通过源码分析共享式AQ...
  • fatherican
  • fatherican
  • 2016年12月15日 18:18
  • 275

JUC源码分析15-集合-ConcurrentHashMap

好几天没看juc了,之前看了HashMap,还有个差不多的HashTable,二者的结构大致相同,小小的比较下2者的不同: 1.HashMap是非线程安全的,HashTable通过synchroni...
  • xiaoxufox
  • xiaoxufox
  • 2016年06月06日 20:04
  • 1392

Java多线程 -- JUC包源码分析4 -- 各种锁与无锁

说到锁,想必很多人就会被如下的各种专业名词绕晕了。本文试图厘清关于锁的各种概念,从而对锁有一个全面而深入的理解。–自旋锁/阻塞锁 –独占锁(排它锁)/共享锁(读写锁) –公平锁/非公平锁 –偏向...
  • chunlongyu
  • chunlongyu
  • 2016年09月03日 21:18
  • 1481

06.JUC 锁 - AQS - 共享模式

基本概念AQS 的共享模式,表示 AQS 通过共享模式获取/释放锁。该类对应的方法为 acquireShared/acquireSharedInterruptibly/tryAcquireShared...
  • u012420654
  • u012420654
  • 2017年02月22日 16:44
  • 308

AQS源码分析(AbstractQueuedSynchronizer)

前言:最近在学习AQS源码,学习过程中也查找过很多资料,但是大都只是翻译了源码的英文意思,并没有对一些难点做出分析和对AQS的队列结构进行讲解。所以我打算写下这篇文章,希望能帮助到有需要的人。 ...
  • wojiaolinaaa
  • wojiaolinaaa
  • 2015年11月27日 14:38
  • 2214

Java多线程 -- JUC包源码分析13 -- Callable/FutureTask源码分析

关于Runnable,我们都已经很熟悉了。在上一篇,我们也分析了ThreadPoolExecutor用来执行任务的接口execute,如下所示:public interface Runnable { ...
  • chunlongyu
  • chunlongyu
  • 2016年09月10日 20:21
  • 812

JUC源码分析20-队列-DelayQueue

画个JUC阻塞队列的类关系图,之前都没在意,画一下感觉会清楚很多 DelayQueue是无界的阻塞队列,其特点是实现队列元素的延迟出队,通俗点说就是队列元素可以设置延迟时间,时间不到,就待在...
  • xiaoxufox
  • xiaoxufox
  • 2016年07月11日 18:03
  • 529
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:JUC源码分析7-locks-AQS-共享模式
举报原因:
原因补充:

(最多只允许输入30个字)