AbstractQueuedSynchronizer源码分析

本文详细分析了Java并发包中的AbstractQueuedSynchronizer(AQS)和LockSupport两个核心类。AQS通过队列实现锁的分配与回收,包括独占锁和共享锁的获取与释放流程。LockSupport提供了线程阻塞与唤醒的基本机制,是实现高级同步器的基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于JDK1.8进行分析的。

在分析AbstractQueuedSynchronizer之前先分析一下LockSupport这个类,以为这个是关于这些锁中最基本的锁。

LockSupport

类继承结构

public class LockSupport

可以看见自己就是一个类,而且无继承无实现。

成员属性

    //unsafe中的,以后常见
    private static final sun.misc.Unsafe UNSAFE;
    //表示内存偏移地址
    private static final long parkBlockerOffset;
    //表示内存偏移地址
    private static final long SEED;
    //表示内存偏移地址
    private static final long PROBE;
    //表示内存偏移地址
    private static final long SECONDARY;

静态代码块

    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> tk = Thread.class;
            parkBlockerOffset = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("parkBlocker"));
            SEED = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSeed"));
            PROBE = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomProbe"));
            SECONDARY = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
        } catch (Exception ex) { throw new Error(ex); }
    }

通过上面代码可以看出,基本都是对Thread的四个偏移地址的成员属性进行初始化。

构造方法

private LockSupport() {}

发现该类的构造方法只有一个,并且被私有化。

成员方法

park

    public static void park(Object blocker) {
        //当前线程
        Thread t = Thread.currentThread();
        //设置blocker
        setBlocker(t, blocker);
        //调用park方法
        UNSAFE.park(false, 0L);
        //重新可运行后再此设置Blocker
        setBlocker(t, null);
    }
    
    public static void park() {
        UNSAFE.park(false, 0L);
    }

整个类中一共有两个关于park的方法,形式也就是重载。底层都是通过调用UNSAFE的park方法实现的。两者的区别在于有没有设置blocker。

其中涉及到方法setBlocker,源码如下:

    private static void setBlocker(Thread t, Object arg) {
        // Even though volatile, hotspot doesn't need a write barrier here.
        UNSAFE.putObject(t, parkBlockerOffset, arg);
    }

这个park方法实现的效果就是阻塞当前线程,然后当调用unpark方法之后,那么在恢复这个线程。但是后者不是安全的,调用的时候,必须要保证恢复的线程存活。

parkNanos

此函数表示在许可可用前禁用当前线程,并最多等待指定的等待时间。

    public static void parkNanos(long nanos) {
        if (nanos > 0)
            UNSAFE.park(false, nanos);
    }
    public static void parkNanos(Object blocker, long nanos) {
        if (nanos > 0) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            UNSAFE.park(false, nanos);
            setBlocker(t, null);
        }
    }

代码和之前的park类似,此处不再赘述。

parkUntil

此函数表示在指定的时限前禁用当前线程,除非许可可用。

    public static void parkUntil(long deadline) {
        UNSAFE.park(true, deadline);
    }
    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(true, deadline);
        setBlocker(t, null);
    }

unpark

此函数表示如果给定线程的许可尚不可用,则使其可用。如果线程在 park 上受阻塞,则它将解除其阻塞状态。否则,保证下一次调用 park 不会受阻塞。如果给定线程尚未启动,则无法保证此操作有任何效果。 

    public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

下面我们回想一下wait和notify的使用。首先来讲,调用wait之后,在调用对应线程的notify方法,可以将该线程从休眠状态改为就绪状态,反之,如果先调用notify,那么再次调用同一个线程的wait方法时,那么此时是不能将该线程转为就绪状态的。然而在该类中则不是这样。我们下面来看一下。

package demo;

import java.util.concurrent.locks.LockSupport;

/**
 * LockSupport代码测试例程 
 * @ClassName:   LockSupportDemo  
 * @Description: TODO
 * @author       BurgessLee
 * @date         2019年4月22日  
 *
 */
public class LockSupportDemo {
	
	public static void main(String[] args) {
		System.out.println("Thead-current:" + Thread.currentThread().getName());
		MyThreadTest myThreadTest = new MyThreadTest(Thread.currentThread());
		myThreadTest.start();
		System.out.println("before park");
        //先调用unpark在调用park打开注释
//		try {
//			Thread.sleep(3000);
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
		//获取许可
        LockSupport.park("LockSupportDemo");
        System.out.println("after park");
	}
	
}

//先调用unpark再调用park结果
//Thead-current:main
//before park
//before unpark
//after unpark
//after park

//先调用unpark再调用park
//class MyThreadTest extends Thread{
//	private Object obj;
//
//	public MyThreadTest(Object obj) {
//		super();
//		this.obj = obj;
//	}
//	
//	@Override
//	public void run() {
//		System.out.println("before unpark");
//		LockSupport.unpark((Thread)obj);
//		System.out.println("after unpark");
//	}
//}

//先调用park在调用unpark结果
//Thead-current:main
//before park
//before unpark
//Blocker info LockSupportDemo
//after park
//Blocker info null
//after unpark

//先调用park在调用unpark
//class MyThreadTest extends Thread{
//	private Object obj;
//	
//	public MyThreadTest(Object obj) {
//		super();
//		this.obj = obj;
//	}
//
//	@Override
//	public void run() {
//		System.out.println("before unpark");
//		try {
//			Thread.sleep(3000);
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//		//获取blocker
//        System.out.println("Blocker info " + LockSupport.getBlocker((Thread) obj));
//        //释放许可
//        LockSupport.unpark((Thread) obj);
//        //休眠500ms,保证先执行park中的setBlocker(t, null);
//        try {
//            Thread.sleep(1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        //再次获取blocker
//        System.out.println("Blocker info " + LockSupport.getBlocker((Thread) obj));
//        System.out.println("after unpark");
//	}
//	
//}

通过上面代码的演示,基本说明了,调用park和unpark的先后顺序颠倒,不会产生wait和notify的效果。具体可以自己本地测试。

当然当线程调用了park方法之后,如果此时调用该线程的interrupt方法,也会产生和调用unpark一样同步的效果。以上大致就是LockSupport整个源码的分析过程了。下面开始看我们的主角AQS。

AbstractQueuedSynchronizer

简单来讲,该类的数据结构是通过队列实现的。默认有一个Sync队列,是一个双向链表的结构,有Head和Tail。另外还有一个Condition队列,当然不是必须的,是一个单向链表。下面我们具体来看看这个类的实现。

继承结构

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer

继承了类AbstractOwnableSynchronizer。后者是一个抽象类,源码如下:

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {

    private static final long serialVersionUID = 3737899427754241961L;

    protected AbstractOwnableSynchronizer() { }

    private transient Thread exclusiveOwnerThread;

    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

    protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }
}

可以看到实现了Serializable接口。主要有一个成员属性exclusiveOwnerThread,也就是独占资源的线程,以及对应有set、get方法。

内部类

Node

static final class Node {
        //共享模式
        static final Node SHARED = new Node();
        //独占模式
        static final Node EXCLUSIVE = null;
        // 结点状态
        // CANCELLED,值为1,表示当前的线程被取消
        // SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark
        // CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中
        // PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行
        // 值为0,表示当前节点在sync队列中,等待着获取锁
        static final int CANCELLED =  1;
        static final int SIGNAL    = -1;
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;
        //结点状态
        volatile int waitStatus;
        //前驱
        volatile Node prev;
        //后继
        volatile Node next;
        //结点所对应的线程
        volatile Thread thread;
        //下一个等待者
        Node nextWaiter;
        //结点是否在共享模式下等待
        final boolean isShared() {
            return nextWaiter == SHARED;
        }
        //获取前驱结点,若前驱结点为空,抛出异常
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }
        //无参构造函数
        Node() {
        }
        //构造函数
        Node(Thread thread, Node mode) {
            this.nextWaiter = mode;
            this.thread = thread;
        }
        //构造函数
        Node(Thread thread, int waitStatus) {
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

ConditionObject

public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;

        /**
         * Creates a new {@code ConditionObject} instance.
         */
        public ConditionObject() { }
        //more....

可以看到该类实现了Condition接口。源码如下:

public interface Condition {
    //等待,当前线程在接到信号或被中断之前一直处于等待状态
    void await() throws InterruptedException;
    //等待,当前线程在接到信号之前一直处于等待状态,不响应中断
    void awaitUninterruptibly();
    //等待,当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态 
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    //等待,当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    //等待,当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态
    boolean awaitUntil(Date deadline) throws InterruptedException;
    //唤醒一个等待线程。如果所有的线程都在等待此条件,则选择其中的一个唤醒。
    //在从await返回之前,该线程必须重新获取锁。
    void signal();
    //唤醒所有等待线程。如果所有的线程都在等待此条件,则唤醒所有线程。
    //在从 await 返回之前,每个线程都必须重新获取锁。
    void signalAll();
}

成员属性

//用于序列化
private static final long serialVersionUID = 7373984972572414691L;
//头结点
private transient volatile Node head;
//尾节点
private transient volatile Node tail;
//状态值
private volatile int state;
//自旋时间
static final long spinForTimeoutThreshold = 1000L;

下面这些在LockSupport中已经介绍过了,此处不再赘述。 

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long stateOffset;
    private static final long headOffset;
    private static final long tailOffset;
    private static final long waitStatusOffset;
    private static final long nextOffset;

    static {
        try {
            stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
            waitStatusOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("waitStatus"));
            nextOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("next"));

        } catch (Exception ex) { throw new Error(ex); }
    }

构造方法

 protected AbstractQueuedSynchronizer() { }

只有一个,而且被protected修饰。

成员方法

首先getState和setState就不在介绍了。可以说主要的核心的方法一共有四个。罗列起来就是:

protected boolean tryAcquire(int arg)
protected boolean tryRelease(int arg)
protected int tryAcquireShared(int arg)
protected boolean tryReleaseShared(int arg)

因为整体是通过队列实现的,那么我首先来看队列操作。

compareAndSetHead(Node update)

设置队列的头结点,在enq方法被调用

    private final boolean compareAndSetHead(Node update) {
        return unsafe.compareAndSwapObject(this, headOffset, null, update);
    }

setHead(Node node)

重新设置队列头节点,只在acquire中被调用

    private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }

 compareAndSetTail(Node expect, Node update)

设置队列尾节点,在enq中有被调用。 

    private final boolean compareAndSetTail(Node expect, Node update) {
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }

 enq(final Node node)

普通节点入队操作,调用方法。在addWaiter被调用

    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // 空节点的时候,这时进行初始化
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {//尾节点不为空,通过CAS添加到队列的尾部
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

  addWaiter(Node mode)

添加当前线程到队列的尾节点。

    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);//当前线程
        Node pred = tail;
        if (pred != null) {//已经被初始化,那么通过CAS添加到队列尾部
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //入队操作
        enq(node);
        return node;
    }

看来的对了的操作,下面我们来看看独占锁具体是怎么实现的。

acquire(int arg)

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

独占模式获取资源,并且中断无效。下次继续。

首先来看这个整体的实现逻辑。

  • 1. 先调用tryAcquire方法,尝试获取独占锁,返回true,表示获取到锁;如果没有获取成功,执行acquireQueued方法。
  • 2. 调用acquireQueued方法,先调用addWaiter方法为当前线程创建一个节点node(上面队列操作中已经介绍过),并插入队列中,
  • 3.然后调用acquireQueued方法去获取锁,如果不成功,就会让当前线程阻塞,当锁释放时才会被唤醒。(后面介绍)
  • 4.acquireQueued方法返回值表示在线程等待过程中,是否有另一个线程调用该线程的interrupt方法,发起中断。(后面介绍)

tryAcquire(int arg)

尝试获取锁,可见这个方法直接抛出异常,而且用protected关键字修饰,是希望子类去实现该方法,否则抛出异常。

    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

该方法的作用是让当前线程尝试获取锁,如果获取到锁,就会返回true,并更改锁资源。没有获取到锁返回false。

上面acquire方法中,如果获取锁成功,那么为当前线程创建一个Node节点的实例执行addWaiter方法入队,之后调用acquireQueued方法尝试去获取锁。该acquireQueued的返回值代表在线程等待过程中,是否有另一个线程调用该线程的interrupt方法,发起中断。如果是,那么直接selfInterrupt执行。

我们先看一下selfInterrupt方法源码。

selfInterrupt()

作用是使得当前线程中断。

    static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }

acquireQueued(final Node node, int arg)

 该方法主要是对当前线程入队后执行尝试去获取锁操作。

    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 {
            // failed为true,表示发生异常,非正常退出
            // 则将node节点的状态设置成CANCELLED,表示node节点所在线程已取消,不需要唤醒了。
            if (failed)
                cancelAcquire(node);
        }
    }

过程大致如下:首先我们看到的主体部分是一个for的死循环,如果当前节点的前驱也就是节点p,如果是头节点,并且成功获取锁,那么直接将当前节点设置为头结点,直接执行。否则,那么会调用shouldParkAfterFailedAcquire和parkAndCheckInterrupt两个方法。为了不影响分析,我们先看一下这两个方法的源码。

shouldParkAfterFailedAcquire(Node pred, Node node) 

方法的返回值决定是否要阻塞当前线程

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        //当前线程上一个节点的状态值
        int ws = pred.waitStatus;
        //如果状态是SINGNAL,那么返回true,当前线程应该被阻塞
        if (ws == Node.SIGNAL)
            return true;
        //如果前一个节点状态是Node.CANCELLED(大于0就是CANCELLED),
        //表示前一个节点所在线程已经被唤醒了,要从队列中移除CANCELLED的节点。
        //所以从pred节点一直向前查找直到找到不是CANCELLED状态的节点,并把它赋值给node.prev,
        //表示node节点的前一个节点已经改变。
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            // 此时前一个节点pred的状态只能是0或者PROPAGATE,不可能是CONDITION状态
            // CONDITION是特殊状态,只在condition列表中节点中存在,队列中不存在这个状态的节点
            // 将前一个节点pred的状态设置成Node.SIGNAL
            //这样在下一次循环时,就是直接阻塞当前线程
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

parkAndCheckInterrupt()

 阻塞当前线程,线程被唤醒后返回当前线程中断状态。

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

 通过源码可以看到,阻塞当前线程,然后当线程被唤醒后,返回线程的中断状态。

cancelAcquire(Node node)

将node节点的状态设置成CANCELLED,表示node节点所在线程已取消,不需要唤醒了。

    private void cancelAcquire(Node node) {
        //数据校验    
        if (node == null)
            return;
        node.thread = null;
        //node之前的节点
        Node pred = node.prev;
        // 跳过那些已取消的节点,在队列中找到在node节点前面的第一次状态不是已取消的节点
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;
        // 记录pred原来的下一个节点,用于CAS函数更新时使用
        Node predNext = pred.next;
        // 将node节点状态设置为已取消Node.CANCELLED;
        node.waitStatus = Node.CANCELLED;
        // 如果node节点是队列尾节点,那么就将pred节点设置为新的队列尾节点
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            int ws;
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                unparkSuccessor(node);// 释放node的前一个结点
            }

            node.next = node; // help GC
        }
    }

下面我们继续看里面调用的方法

unparkSuccessor(Node node)

释放后继节点

    private void unparkSuccessor(Node node) {
        //node的状态
        int ws = node.waitStatus;
        //为SIGNAL -1 或 CONDITION -2 或 PROPAGATE -3
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        //获取node节点的下一个结点
        Node s = node.next;
        //下一个结点为空或者下一个节点的等待状态大于0,即为CANCELLED
        if (s == null || s.waitStatus > 0) {
            s = null;
            //向前寻找节点
            for (Node t = tail; t != null && t != node; t = t.prev)
                //找到等待状态小于等于0的结点,找到最前的状态小于等于0的结点
                if (t.waitStatus <= 0)
                    s = t;
        }
        //该结点不为为空,释放许可
        if (s != null)
            LockSupport.unpark(s.thread);
    }

以上也就是整个acquire独占锁实现的过程。下面我们继续分析release释放独占锁的过程。

release(int arg) 

独占模式下,释放独占锁。

    public final boolean release(int arg) {
        // 调用tryRelease方法,尝试去释放锁,由子类具体实现
        if (tryRelease(arg)) {
            //如果队列头节点的状态不是0,那么队列中就可能存在需要唤醒的等待节点。
            //还记得我们在acquireQueued(final Node node, int arg)获取锁的方法中,
            //如果节点node没有获取到锁,
            //那么我们会将节点node的前一个节点状态设置为Node.SIGNAL,
            //然后调用parkAndCheckInterrupt方法
            //将节点node所在线程阻塞。
            //在这里就是通过unparkSuccessor方法,
            //进而调用LockSupport.unpark(s.thread)方法,唤醒被阻塞的线程
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

从源码可以看出调用了tryRelease方法,源码如下:

tryRelease(int arg)

可见和tryAcquire方法一样,需要子类去实现,否则抛出异常。

    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

返回true表示锁资源完全释放了,返回false表示还持有锁资源。

注:对于独占锁来说,同一时间只能有一个线程持有这个锁,但是这个线程可以重复地获取锁,因为被锁住的模块,再次进入另一个被这个锁锁住的模块,是允许的。这个就做可重入性,所以对于可重入的锁释放操作,也需要多次。

代码中unparkSuccessor方法在前面已经介绍过了,所以此处不再赘述。下面我们来看共享锁。涉及到的方法自然就是acquireShared。源码如下:

acquireShared(int arg)

用来获取共享锁。

    public final void acquireShared(int arg) {
        //尝试去获取共享锁,如果返回值小于0表示获取共享锁失败
        if (tryAcquireShared(arg) < 0)
            //继续获取共享锁
            doAcquireShared(arg);
    }

我们接着看源码中涉及调用的代码。

tryAcquireShared(int arg)

可见和acquire方法类似。需要子类实现,否则抛出异常。返回值大于等于0,表示获取共享锁成功。

    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

doAcquireShared(int arg) 

  获取共享锁,获取失败,则会阻塞当前线程,直到获取共享锁返回

    private void doAcquireShared(int arg) {
        //为当前线程创建共享锁节点node
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    //如果节点node前一个节点是同步队列头节点。
                    //就会调用tryAcquireShared方法尝试获取共享锁
                    int r = tryAcquireShared(arg);
                    //如果返回值大于0,表示获取共享锁成功
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                //如果节点p的状态是Node.SIGNAL,
                //就是调用parkAndCheckInterrupt方法阻塞当前线程
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            //failed为true,表示发生异常,
            //则将node节点的状态设置成CANCELLED,表示node节点所在线程已取消,不需要唤醒了
            if (failed)
                cancelAcquire(node);
        }
    }

与独占锁相比来看,不同如下:

  • doAcquireShared方法,调用addWaiter(Node.SHARED)方法,为当前线程创建一个共享模式的节点node。而acquireQueued方法是由外部传递来的。
  • doAcquireShared方法没有返回值,acquireQueued方法会返回布尔类型的值,是当前线程中断标志位值
  • 最大的区别是重新设置队列头的方法不一样。doAcquireShared方法调用setHeadAndPropagate方法,而acquireQueued方法调用setHead方法。

setHeadAndPropagate(Node node, int propagate)

重新设置队列头,如果队列头的下一个节点为null或者共享模式,那么就要唤醒共享锁上等待的线程

    private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head;
        //重新设置对头节点
        setHead(node);
        // 如果propagate大于0
        if (propagate > 0 || 
            h == null || 
            h.waitStatus < 0 ||
            (h = head) == null || 
            h.waitStatus < 0) {
            Node s = node.next;
            //如果节点s是空或者共享模式节点,那么就要唤醒共享锁上等待的线程
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

releaseShared(int arg)

作用是释放共享锁 

    public final boolean releaseShared(int arg) {
        // 尝试释放共享锁
        if (tryReleaseShared(arg)) {
            // 唤醒等待共享锁的线程
            doReleaseShared();
            return true;
        }
        return false;
    }

tryReleaseShared(int arg)

 需要子类去实现,否则抛出异常。

    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

返回true表示已经完全释放锁资源,返回false,表示还持有锁资源。

doReleaseShared()

执行释放动作。

    private void doReleaseShared() {
        //死循环
        for (;;) {
            //头节点
            Node h = head;
            //队列中有其他元素
            if (h != null && h != tail) {
                //得到节点h的状态
                int ws = h.waitStatus;
                // 如果状态是Node.SIGNAL,就要唤醒节点h后继节点的线程
                if (ws == Node.SIGNAL) {
                    //将节点h的状态设置成0,如果设置失败,就继续循环,再试一次。
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;
                    //唤醒节点h后继节点的线程
                    unparkSuccessor(h);
                }
                //如果节点h的状态是0,就设置ws的状态是PROPAGATE。
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;
            }
            //如果同步队列头head节点发生改变,继续循环,
            //如果没有改变,就跳出循环
            if (h == head)
                break;
        }
    }

代码中注释内容已经很清楚,此处不再赘述。

剩下的方法await和singal以及singalAll以后在介绍。看了这么多源码,接下来看看DEMO演示。

package demo;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.junit.Test;

/**
 * AbstractQueuedSyncrhonizer代码测试
 * @ClassName:   AbstractQueuedSyncrhonizerDemo  
 * @Description: TODO
 * @author       BurgessLee
 * @date         2019年4月23日  
 *
 */
public class AbstractQueuedSyncrhonizerDemo {
	
	@Test
	public void test01() {
		Lock lock = new ReentrantLock();
//		for(int i = 0; i < 100; i ++) {
//			MyThreadTest t = new MyThreadTest("thread-" +i, lock);
//			t.start();
//		}
		MyThreadTest t1 = new MyThreadTest("thread-1",lock);
		MyThreadTest t2 = new MyThreadTest("thread-2",lock);
		t1.start();
		t2.start();
	}
	
}

class MyThreadTest extends Thread{
	private Lock lock;

	public MyThreadTest(String name,Lock lock) {
		super(name);
		this.lock = lock;
	}
	
	@Override
	public void run() {
		lock.lock();
		try {
			System.out.println(Thread.currentThread() + "running....");
		}finally {
			lock.unlock();
		}
	}
}

输出结果如下:

Thread[thread-1,5,main]running....
Thread[thread-2,5,main]running....

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值