LinkedBlockingQueue源码分析

LinkedBlockingQueue是基于链表的阻塞队列,其类定义如下所示

static class Node<E> {//节点的定义
        E item;

        Node<E> next;

        Node(E x) { item = x; }
    }

    /** 队列的容量,默认为Integer.MAX_VALUE */
    private final int capacity;

    /** 队列中元素的个数 */
    private final AtomicInteger count = new AtomicInteger(0);

    /**
     * 队列的头指针,take()操作修改头指针,并由锁takeLock 保护
     * Invariant: head.item == null
     */
    private transient Node<E> head;

    /**
     * 队列的尾指针,put()操作修改尾指针,由锁putLock 来保护
     * Invariant: last.next == null
     */
    private transient Node<E> last;

    /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

    /** Lock held by put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();

在LinkedBlockingQueue类的实现中,很重要的一个和ArrayBlockingQueue不同的地方,是对put()和take()分别使用了两个不同的锁,put()使用了putLock来同步,修改尾指针last。take()使用takeLock来同步,修改头指针head。而针对“空”和“满”的阻塞条件,也是对这两个所对象分别构建的两个Condition对象(notEmpty和notFull),构成了双锁双条件。
对于put()和take()以及类似的操作,双锁避免了互相影响,一定意义上看,减小了操作的锁粒度,提高了并发性。
put()操作如下:

 public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        int c = -1;
        Node<E> node = new Node(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {

            while (count.get() == capacity) {
                notFull.await();//队列满时,阻塞于notFull条件变量
            }
            enqueue(node);//添加元素,修改last指针
            c = count.getAndIncrement();
            //比较迷惑的是下面的if语句,该语句用来唤醒阻塞的put线程,下面做详细介绍
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
    }

take()操作

 public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            while (count.get() == 0) {
                notEmpty.await();
            }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

在ArrayBlockingQueue中put线程在每次put()完后,都会调用 notEmpty.signal(),唤醒阻塞于notEmpty上的take()线程。而在LinkedBlockingQueue中唤醒操作主要发生在同类型线程之间,put线程唤醒put线程,take线程唤醒take线程。
例如:队列容量为5,当前队列满:
【1】->【2】->【3】->【4】->【5】
此时来了3个put线程,put1,put2,put3.因为队列满,所以三个put线程全部阻塞于notFull上。然后来了3个take线程,take1,take2,take3。当take1线程执行到take()操作中的if (c == capacity) signalNotFull();时唤醒一个阻塞的put线程,假设put1线程被唤醒。此时count=4。接着take2线程执行take()操作,由于count=4 , take2线程不会唤醒任何put线程。接着take3线程执行take()操作,由于count=3, take3线程不会唤醒任何put线程。此时count=2,接下来put1线程执行put()操作,c的值为2,执行if (c + 1 < capacity) notFull.signal();唤醒阻塞的put2线程。put2线程执行put()操作,c的值为3,执行if (c + 1 < capacity) notFull.signal();唤醒阻塞的put3线程.
由以上例子可以看出,唤醒操作主要发生在同类型操作之间。
注意:调用条件变量的await,signal时,前提是线程必须获取到于条件变量相关联的锁。

内容概要:本文详细探讨了基于樽海鞘算法(SSA)优化的极限学习机(ELM)在回归预测任务中的应用,并与传统的BP神经网络、广义回归神经网络(GRNN)以及未优化的ELM进行了性能对比。首先介绍了ELM的基本原理,即通过随机生成输入层与隐藏层之间的连接权重及阈值,仅需计算输出权重即可快速完成训练。接着阐述了SSA的工作机制,利用樽海鞘群体觅食行为优化ELM的输入权重和隐藏层阈值,从而提高模型性能。随后分别给出了BP、GRNN、ELM和SSA-ELM的具体实现代码,并通过波士顿房价数据集和其他工业数据集验证了各模型的表现。结果显示,SSA-ELM在预测精度方面显著优于其他三种方法,尽管其训练时间较长,但在实际应用中仍具有明显优势。 适合人群:对机器学习尤其是回归预测感兴趣的科研人员和技术开发者,特别是那些希望深入了解ELM及其优化方法的人。 使用场景及目标:适用于需要高效、高精度回归预测的应用场景,如金融建模、工业数据分析等。主要目标是提供一种更为有效的回归预测解决方案,尤其是在处理大规模数据集时能够保持较高的预测精度。 其他说明:文中提供了详细的代码示例和性能对比图表,帮助读者更好地理解和复现实验结果。同时提醒使用者注意SSA参数的选择对模型性能的影响,建议进行参数敏感性分析以获得最佳效果。
《芋道开发指南文档-2023-10-27更新》是针对软件开发者和IT专业人士的一份详尽的资源集合,旨在提供最新的开发实践、范例代码和最佳策略。这份2023年10月27日更新的文档集,包含了丰富的模板和素材,帮助开发者在日常工作中提高效率,保证项目的顺利进行。 让我们深入探讨这份文档的可能内容。"芋道"可能是一个开源项目或一个专业的技术社区,其开发指南涵盖了多个方面,例如: 1. **编程语言指南**:可能包括Java、Python、JavaScript、C++等主流语言的编码规范、最佳实践以及常见问题的解决方案。 2. **框架与库的应用**:可能会讲解React、Vue、Angular等前端框架,以及Django、Spring Boot等后端框架的使用技巧和常见应用场景。 3. **数据库管理**:涵盖了SQL语言的基本操作,数据库设计原则,以及如何高效使用MySQL、PostgreSQL、MongoDB等数据库系统。 4. **版本控制**:详细介绍了Git的工作流程,分支管理策略,以及与其他开发工具(如Visual Studio Code、IntelliJ IDEA)的集成。 5. **持续集成与持续部署(CI/CD)**:包括Jenkins、Travis CI、GitHub Actions等工具的配置和使用,以实现自动化测试和部署。 6. **云服务与容器化**:可能涉及AWS、Azure、Google Cloud Platform等云计算平台的使用,以及Docker和Kubernetes的容器化部署实践。 7. **API设计与测试**:讲解RESTful API的设计原则,Swagger的使用,以及Postman等工具进行API测试的方法。 8. **安全性与隐私保护**:涵盖OAuth、JWT认证机制,HTTPS安全通信,以及防止SQL注入、
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值