博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AbstractQueuedSynchronizer源码阅读
阅读量:7211 次
发布时间:2019-06-29

本文共 3721 字,大约阅读时间需要 12 分钟。

hot3.png

AbstractQueuedSynchronizer 就是那个大名鼎鼎的 AQS,是java.util.concurrent包下同步器的核心。

CLH(Craig, Landin, and Hagersten)锁

使用队列的方式来解决n个线程来争夺m把锁的问题,每当一个新的线程需要获取锁,为其创建一个节点并放到队尾,如果该线程是队列中的第一个节点,则节点的locked设置成false,如果它不是队列的第一个节点,则它的节点的prev指向原来的队尾节点,并不断自旋查看prev指向节点的locked属性,如果该值变为false,表示轮到它来尝试获取锁了,如果获取成功并最终用完释放后,则将自己的locked设置成false,如果获取失败,locked值不变,还是true,并不断尝试获取锁。

也就是说,每个节点只需要关心前置节点的locked状态,可以发现CLH实现锁的获取是公平的。

Node节点

Node节点维护了双向节点和当前节点状态和线程引用。

static final class Node {volatile Node prev;volatile Node next;volatile Thread thread;volatile int waitStatus; // SIGNAL CANCELLED CONDITION PROPAGATECANCELLED,值为1,表示当前的线程被取消;SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;值为0,表示当前节点在sync队列中,等待着获取锁。}

FIFO队列

*      +------+  prev +-----+       +-----+ * head |      | <---- |     | <---- |     |  tail *      +------+       +-----+       +-----+

核心属性

  • private transient volatile Node head; 等待队列首元素;
  • private transient volatile Node tail; 等待队列尾元素;
  • private volatile int state; 同步状态;

需要在锁定时,需要维护一个状态(int类型),而对状态的操作是原子和非阻塞的,通过同步器提供的对状态访问的方法对状态进行操纵,并基于Unsafe的原子操作来修改state的状态,compareAndSet来确保原子性的修改。

获取/设置当前的同步状态:

protected final int getState() {        return state;    }protected final void setState(int newState) {        state = newState;    }

对同步状态对修改采用原子操作:

protected final boolean compareAndSetState(int expect, int update) {        // See below for intrinsics setup to support this        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);    }

入队操作,如果队列为空则实例化节点,否则插入队尾:

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;    }
  • protected boolean tryAcquire(int arg) 排它的获取这个状态。这个方法的实现需要查询当前状态是否允许获取,然后再进行获取(使用compareAndSetState来做)状态。
  • protected boolean tryRelease(int arg) 释放状态。
  • protected int tryAcquireShared(int arg) 共享的模式下获取状态。
  • protected boolean tryReleaseShared(int arg) 共享的模式下释放状态。
  • protected boolean isHeldExclusively() 在排它模式下,状态是否被占用。

获取锁: 需要获取当前节点的前驱节点,并且头结点能够获取状态,代表能够占有锁。

final boolean acquireQueued(final Node node, int arg) {        boolean failed = true;        try {            boolean interrupted = false;            for (;;) {                final Node p = node.predecessor();                if (p == head && tryAcquire(arg)) {                    setHead(node);                    p.next = null; // help GC                    failed = false;                    return interrupted;                }                if (shouldParkAfterFailedAcquire(p, node) &&                    parkAndCheckInterrupt())                    interrupted = true;            }        } finally {            if (failed)                cancelAcquire(node);        }    }

这个方法在非公平实现中,主要是通过AQS的state来检查和维护锁状态,如果state是0,说明没有线程占有这个锁,如果不为0并且锁的占有线程是当前线程,则是重入的情况,均可以获得锁并修改state值。

如果是首次获得锁,则设置锁占有线程为当前线程。

当然,如果前面两种情况都不满足,说明尝试获得锁失败,需要做前面段落所述的队列操作,创建一个等待结点并进入循环,循环中的park()调用挂起当前线程。

否则将当前线程挂起:

LockSupport.park最终把线程交给系统(Linux)内核进行阻塞。

private final boolean parkAndCheckInterrupt() {        LockSupport.park(this);        return Thread.interrupted();    }

释放锁

public final boolean release(int arg) {        if (tryRelease(arg)) {            Node h = head;            if (h != null && h.waitStatus != 0)                unparkSuccessor(h);            return true;        }        return false;    }

如果修改state值成功,则找到队列中应该唤起的结点,对节点中的线程调用unpark()方法,恢复线程执行。

转载于:https://my.oschina.net/u/1000241/blog/1608815

你可能感兴趣的文章
SSDT&Shadow Hook的实现,完整代码。可编译
查看>>
Spring4-自动装配Beans-通过注解@Autowired在构造方法上
查看>>
MapReduce编程(四) 求均值
查看>>
ASP.NET MVC在IIS6下部署的小技巧
查看>>
asp.net 递归删除文件夹及其子文件夹和所有文件[转]
查看>>
TCP端口状态说明ESTABLISHED、TIME_WAIT、 CLOSE_WAIT
查看>>
Bengio:我留在学术圈为全人类作贡献,而不是为某一个公司赚钱
查看>>
100多个经典常用的PHP功能插件大全实例演示和下载
查看>>
Mac 下iterm2配色方案(高亮)及显示分支
查看>>
使用<meta>来刷新网页效果
查看>>
VR为难民发声,传递人道主义精神
查看>>
基准测试工具
查看>>
遇到的几个开机启动故障
查看>>
NEC向格鲁吉亚提供基于面部识别技术的城市监控系统
查看>>
linux网络编程之-----基础理论篇
查看>>
加大Linux服务器的文件描述符
查看>>
Linux内核编译过程详解
查看>>
shell监控web服务的多种方案
查看>>
微信小程序--亲戚称呼计算
查看>>
Broker模块划分
查看>>