非公平锁加锁
2023-08-20 14:04:26 2 举报
非公平锁加锁
作者其他创作
大纲/内容
Node 这个数据结构就是等待队列中的节点,记录了等待线程是当前线程,mode 此时为 null,先不管;Node 有 prev 和 next 两个属性,可以判断等待队列是个双向链表;Node 还有一个 int 属性 waitStatus,在初始化时 int 类型默认值为 0;waitStatus 取值含义如下:- 0,初始值;- 1,CANCELLED,表示线程已经取消;- -1,SIGNAL,表示需要 unpark 后继线程;- -2,CONDITION,表示线程在等待条件 Condition;- -3,PROPAGATE,表示下一个 acquireShared 应当可以获取成功,acquireShared 用于获取共享锁,如读锁;
首次执行 shouldParkAfterFailedAcquire 通常会返回 false,再次执行才返回 true;这里的 for 循环,尽可能在阻塞之前 tryAcquire 获取锁;原因在于 tryAcquire 中通过 cas 获取锁性能较高,而一旦发生线程阻塞/唤醒之后,会有操作系统上下文切换,性能不高;一句话总结:能够 cas 时,尽量不去 park;
final boolean nonfairTryAcquire(int acquires) { // 顾名思义,该方法就是非公平的尝试获取锁 final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { font color=\"#569230\
enq(node)
addWaiter:等待队列的初始化与节点入队;acquireQueued:尝试从等待队列中唤醒指定 node 节点;
public final void acquire(int arg) { if (!tryAcquire(arg) && // 尝试获取锁 acquireQueued(font color=\"#ec7270\
这里就是非公平锁的体现,每次 lock() 时先做一次 cas 尝试直接获取锁,如果此时锁被别的线程释放是有可能直接拿到的,相当于插队;
font color=\"#ec7270\
private static boolean font color=\"#ec7270\
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
park 后线程被阻塞;park 是可以被唤醒的(除了 unpark 外),当有别的线程修改该线程的中断标志位时,阻塞就会失效,线程被唤醒并继续向下执行;
final boolean font color=\"#ec7270\
acquire(1)
tryAcquire(1)
nonfairTryAcquire(1)
Node.SIGNAL 仅仅是一个可唤醒标记,可以理解为阻塞 node 前的准备;如果 node 没有被阻塞,是不需要唤醒的;
队列初始化时,将 head 节点设置为一个 new Node(),其实就是指向拿到锁的线程,不需要设置属性 thread,因为可以通过 exclusiveOwnerThread 属性获得持有锁的线程;新入队的 node(参数 node),插入到原来 tail 节点后;如果这里进行了队列初始化,其实就是 head 后面的第一个节点;
final void font color=\"#ec7270\
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); // 阻塞线程 return Thread.interrupted(); }
private Node enq(final Node node) { for (;;) { // 这个 for 循环其实就是自旋,只有 node 入队成功才会跳出 Node t = tail; if (t == null) { // 再次判断是否有等待队列 // 在这个 if 下初始化等待队列,但是并不会跳出 for 循环,只是 t 不再为 null if (compareAndSetHead(new Node())) font color=\"#569230\
非公平锁 lock()
private Node addWaiter(Node mode) { Node node = new font color=\"#ec7270\
0 条评论
下一页