AQS一点点的学习

本文介绍了AQS中队列的懒初始化实现方式,通过使用compareAndSet操作确保线程安全地进行节点添加。首次竞争时创建头节点,并在循环中不断尝试将新节点设置为尾节点。

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

 

private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

 

 

这是AQS中的一小部分,函数功能是在队列中插入一个节点,AQS队列,为了节省开销,都是在用懒初始化的模式,在往队列里面插入的时候,才去初始化头节点和尾节点。原文注释

 

CLH queues need a dummy header node to get started. But we don't create them on construction, because it would be wasted effort if there is never contention. Instead, the node is constructed and head and tail pointers are set upon first contention. 

 

先假设只有一个线程,第一次进入到这段代码时,进入到第4行,然后正确的设置了head节点,相当于head,tail都指向一个新new出来的node节点,由于是死循环,程序会运行到第7行,运行compareAndSetTail(t, node)后,tail指向新的尾节点,就是函数传入的入参node,node的prev指针指向原来的tail,原来的tail的next指针指向node

 

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

 以上代码原子的设置当前对象的head字段,思想上,类似数据库的乐观锁,set head = update where head=null, 多线程模式下,只有一个线程能够设置成功,类似的向队列尾巴,添加一个节点,也只有一个线程能够成功,如果没有成功,由于调用程序写的是死循环,会向新的尾巴上增加节点,直到成功为止

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

 

 总结,这段代码很巧妙的避免了多线程问题,将一个节点加入到队列中,并用了懒初始化,头节点和尾节点

 

 

### Java AQS (AbstractQueuedSynchronizer) 使用指南 #### AQS简介 AQS 是 Java 并发包中的核心组件之一,提供了一种用来构建锁和其他同步器的基础框架。该类位于 `java.util.concurrent.locks` 包下,并且实现了 FIFO 队列来管理线程等待队列[^1]。 #### 继承结构 AQS 和 AQLS (AbstractQueuedLongSynchronizer)都继承了名为 AOS(AbstractOwnableSynchronizer)的一个抽象类。此父类引入于 JDK 1.6 版本中,主要用于表示锁与其拥有者间的关系,在独占模式下调用方可以设置当前占有锁的对象实例。 #### 设计灵活性 为了适应不同的应用场景需求,AQS 提供了一系列模板方法让开发者能够方便地创建自定义同步工具。这些方法包括但不限于: - **tryAcquire(int arg)**:尝试获取独占式的资源; - **tryRelease(int arg)** :尝试释放已持有的独占式资源; - **tryAcquireShared(int arg)** : 尝试获得共享类型的许可; - **tryReleaseShared(int arg)** : 放弃之前得到过的共享权限; 上述四个函数都需要由具体的子类去重写实现,从而定义各自独特的同步机制[^3]。 #### 实际应用案例 - CountDownLatch 作为 AQS 应用的经典范例之一,`CountDownLatch` 展现了如何利用 AQS 来处理多线程间的协作问题。此类允许一个或多个线程一直阻塞直到其他一些线程完成一系列操作之后再继续执行下去。下面给出一段简单的代码片段展示其基本用法: ```java import java.util.concurrent.CountDownLatch; public class Worker implements Runnable { private final CountDownLatch startSignal; private final CountDownLatch doneSignal; public Worker(CountDownLatch startSignal, CountDownLatch doneSignal){ this.startSignal = startSignal; this.doneSignal = doneSignal; } @Override public void run(){ try{ System.out.println(Thread.currentThread().getName()+" is waiting"); startSignal.await(); // wait until all threads are ready. doWork(); System.out.println(Thread.currentThread().getName()+" has finished work."); doneSignal.countDown();// signal that the current thread has completed its task. }catch(Exception e){ throw new RuntimeException(e); } } protected void doWork(){} } // Usage example: int nThreads = 5; CountDownLatch startGate = new CountDownLatch(nThreads); CountDownLatch endGate = new CountDownLatch(nThreads); for(int i=0;i<nThreads;++i){ Thread t=new Thread(new Worker(startGate,endGate),"Worker-"+i); t.start(); } startGate.countDown(); // release all workers at once endGate.await(); // main waits here for them to finish their jobs System.out.println("All tasks have been processed!"); ``` 这段程序展示了五个工作线程在接收到启动信号前处于挂起状态,当最后一个工作者准备好后它们会同时开始运行各自的作业并最终通知主线程所有任务已完成[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值