并行化资源池队列3——紧密相关的同步化队列

本文介绍了一种紧密相关的同步队列实现方式,其中生产者线程产生的数据由消费者线程按先进先出的原则消费,同时确保生产者和消费者之间相互等待。文章详细描述了两种同步队列的实现方案,包括基于管程的实现和基于双重数据结构的实现。

紧密相关的同步化队列

现在来看一种紧密相关的同步方式。一个或多个生产者线程生产数据,并由一个或多个消费者线程按照先进先出的次序来获取。但是,生产者与消费者之间必须相互回合,即向队列中放入一个元素的生产者应阻塞直到该元素被另外一个消费者取出,反之亦然。其实现的伪代码描述如下:

publicclassSynchronousQueue<T>{

      T item=null;

      booleanenqueuing;

      Lock lock;

      Condition condition;

      publicvoidenq(T value){

         lock.lock();

         try{

            while(enqueuing){

               condition.await();

            }

            enqueuing=true;

            item=value;

            condition.signalAll();

            while(item!=null)

               condition.await();

            enqueuing=false;

            condition.signalAll();

         }finally{

            lock.unlock();

         }

      }

      public T deq(){

         lock.lock();

         try{

            while(item==null)

               condition.await();

            T t=item;

            item=null;

            condition.signalAll();

            return t;

         }finally{

            lock.unlock();

         }

      }

   }

    这是一种基于管程的同步队列实现。由于这个队列设计非常简单,所以他的同步代价很高。在每个线程可能唤醒另一个线程的时间点,无论是入队者还是出队者都会唤醒所有的等待线程,从而唤醒的次数是等待线程数目的平方。尽管可以使用条件对象来减少唤醒次数,但由于仍需要阻塞每次调用,所以开销很大。

    为了减少同步队列的同步开销,我们考虑另外一种同步队列的实现,在该实现中将入队与出队分两步完成。如出队从一个空队列删除元素的过程为,第一步,他将一个保留对象放入队列,表示该出队者这在等待一个准备与之会合的入队者。然后,出队者在这个保留对象的flag标志上自旋等待。第二步,当一个入队者发现该保留时,他通过存放一个元素并设置保留对象的flag来通知出队者完成保留。同样,入队者也能够通过创建自己的保留对象,并在保留对象的flag标志上自旋来等待会合同伴。在任意时刻,队列本身或者包含出队者的保留对象或者包含入队者的保留对象,或者为空。因此操作队列的线程共有4种交互对象,即入队者线程、入队者保留、出队者线程、出队者保留,由于需要进行紧密的同步,因此他们之间的对应关系如下:

入队者线程------------>出队者保留;

出队者线程------------>入队者保留;

    即入队者线程要协助处理出队者保留,同样出队者线程要协助处理入队者保留。这种结构称为双重数据结构,其核心是方法是通过两个步骤来生效的:保留和完成。该结构具有很多很好的性质。首先,正在等待的线程可以在一个本地缓存标志上自旋,而这时可扩展性的基础。其次,他很自然的保证了公平性。保留按照他们到达的次序来排队,从而保证请求也按照同样的次序完成,因此这种结构是可以线性化的,因为每个部分方法调用在他完成时是可以排序的。

    该队列结构可以用节点组成的链表来实现,其中节点或者表示一个等待出队的元素或者表示一个等待完成的保留,由节点的Type域指定。任何时候,所有的队列节点都应具备相同的类型,即或者全部是等待出队的元素,或者全部是等待完成的保留。当一个元素入队时,节点的item域存放该元素;当元素出队时,节点的item域被重新设置为null。当一个保留入队时,节点的item域为null;当保留被一个入队者完成时,节点的item域被重新设置为一个元素。

    因此首先定义一个Java枚举,来表示节点的类型,然后定义链表的节点元素,在整个实现过程中,依然基于Java的原子化操作来实现并发控制,所以源代码如下所示:

publicenumNodeType {

 ITEM,RESERVATION;

}

其中,ITEM代表节点元素,RESERVATION代表保留对象。

import java.util.concurrent.atomic.AtomicReference;

publicclassSynNode<E> {

  volatileNodeType type;//节点类型

//节点元素,元素值为Java泛化类型

  volatileAtomicReference<E> item;

  volatileAtomicReference<SynNode<E>> next;

  SynNode(E eitem,NodeType etype){

      item=newAtomicReference<E>(eitem);

      next=newAtomicReference<SynNode<E>>(null);

      type=etype;

  }

}

接下来定义队列的主体实现,其中主要包括:入队和出对操作。

import java.util.concurrent.atomic.AtomicReference;

publicclassSynchronousDualQueue<E> {

  privateAtomicReference<SynNode<E>> head,tail;//头尾哨兵节点

  publicSynchronousDualQueue(){

//初始化空队列,创建一个具有任意值的节点,并让头尾指针指向该节点

     SynNode<E> snode=new SynNode<E>(null,NodeType.ITEM);

     head=newAtomicReference<SynNode<E>>(snode);

     tail=newAtomicReference<SynNode<E>>(snode);

  }

//入队操作

  publicvoidenq(E item){

//创建将被入队的新节点

      SynNode<E> offer=newSynNode<E>(item,NodeType.ITEM);

      while(true){

//获取头尾哨兵节点

         SynNode<E> t=tail.get();

         SynNode<E> h=head.get();

//检验队列是否为空或者是否包含已入队元素的出队保留

         if(t==h||t.type==NodeType.ITEM){

//读取tail节点的后继

            SynNode<E> n=t.next.get();

//判断读取的tail值是一致的,即tail的状态不会被并发线程更改

            if(t==tail.get()){

//如果tail域没有指向队列的最后一个节点,则尝试推进tail,并重新开始

               if(n!=null){

//尝试推进tail

                   tail.compareAndSet(t,n);

               }

//如果tail域就是队列最后一个元素,那么尝试将tail的后继指向新增节点,//即将新增节点挂接到队尾

                 elseif(t.next.compareAndSet(n,offer)){

//如果成功挂接,那么尝试推进tail域,使其指向新增节点

                   tail.compareAndSet(t,offer);

//自旋等待,等待一个出队者通过设置该节点的item域为null来通知该元素已//经出队。这是因为这是一个紧密同步的队列,入队元素必须等待使用它的出队//

                   while(offer.item.get()==item);

//一旦出队成功,就尝试移动头指针,以便进行清理。这是因为出队是从头节点//出队。通过将新增节点设置为head哨兵节点,实现对被使用节点的清理

                   h=head.get();

                   if(offer==h.next.get()){

                      head.compareAndSet(h,offer);

                   }

                   return;

               }

            }

         }

//如果存在正在等待完成的出队者的保留,那么就找出一个保留并完成它

         else{

//读取head的后继节点

           SynNode<E> node=h.next.get();

//判断读到的值是一致的,即运行过程中状态的一致性不能被并发线程所破坏,//主要通过尾指针是否一致、头指针是否一致、以及首节点是否为空来判断,这//些状态都可能在运行中被并发线程改变,因此只要有一个状态被改变,即认为//整体状态被破坏了,需要重新开始

           if(t!=tail.get()||h!=head.get()||node==null){

              continue;

           }

//如果状态一致,那么尝试着将节点的item域从null改为要入队的元素。因为//出队者保留等待的是一个入队者,所以要将item域由null置为入队元素的//item

           boolean success=node.item.compareAndSet(null,item);

//不管上一步是否成功,都尝试推进head

           head.compareAndSet(h,node);

//如果推进head成功,该方法返回,否则重试

           if(success){

              return;

           }

         }

      }

  }

//出队操作,其操作逻辑与入队基本相似,在此不再赘述

public E deq(){

      E result=null;

      while(true){

         SynNode<E> h=head.get();

         SynNode<E> t=tail.get();

         if(h==t || h.type==NodeType.ITEM){

            SynNode<E> n=h.next.get();

            if(h==head.get()){

               if(n==null){

                   tail.compareAndSet(t,new SynNode<E>(null,NodeType.RESERVATION));

               }elseif(head.compareAndSet(h,n)){

                   while(n.item.get()==null);

                   t=tail.get();

                   if(n==t.next.get()){

                      result=n.item.get();

                      tail.compareAndSet(t,n);

                   }

                   return result;

               }

            }

         }

         else{

            SynNode<E> node=t.next.get();

            result=node.item.get();

            if(t!=tail.get()||h!=head.get()||node==null){

               continue;

            }

            boolean success=node.item.compareAndSet(result,null);

            tail.compareAndSet(t,node);

            if(success){

               return result;

            }

         }

      }

  }

}
内容概要:本文围绕“基于蓄电池进行调峰和频率调节研究”,提出了一种采用超线性增益的联合优化方法,并通过Matlab代码实现相关仿真与验证。研究聚焦于电力系统中蓄电池在调峰(平衡负荷波动)和频率调节(维持电网稳定)双重功能下的协调优化问题,通过构建数学模型,引入超线性增益机制提升控制精度与响应效率,从而提高储能系统的运行经济性与电网稳定性。文中还提供了完整的代码资源链接,便于复现与进一步研究,属于电力系统智能管理领域的典型技术应用。; 适合人群:具备电力系统基础知识、储能技术背景及一定Matlab编程能力基于蓄电池进行调峰和频率调节研究【超线性增益的联合优化】(Matlab代码实现)的高校研究生、科研人员或从事新能源系统优化的工程技术人员;尤其适合开展储能调度、微电网优化等相关课题研究的学习者。; 使用场景及目标:① 掌握蓄电池在电力系统调峰与调频中的协同工作机制;② 学习并复现基于超线性增益的联合优化算法设计与建模方法;③ 利用Matlab/Simulink平台实现储能系统优化控制策略的仿真分析,服务于科研论文复现、项目开发或算法改进。; 阅读建议:建议结合文中提到的YALMIP工具包和网盘资源,边阅读边运行代码,重点关注目标函数构建、约束条件设置及优化求解流程;同时可对比传统线性增益方法,深入理解超线性增益带来的性能提升机制。
内容概要:本文介绍了一种基于稀疏贝叶斯学习(SBL)的轴承故障诊断方法,提出两种群稀疏学习算法用于提取故障脉冲信号。第一种算法仅利用故障脉冲的群稀疏性,第二种进一步结合其周期性行为,以提升故障特征提取的准确性与鲁棒性。文档提供了完整的Matlab代码实现,适用于振动信号分析与早期故障检测,具有较强的工程应用价值。此外,文中还附带了多个科研领域的仿真资源链接,涵盖电力系统、信号处理、机器学习、路径规划等多个方向,突出MATLAB在科研仿真中的广泛应用。; 适合人群:具备一定信号处理或机械故障诊断基础,熟悉Matlab编程,从【轴承故障诊断】一种用于轴承故障诊断的稀疏贝叶斯学习(SBL),两种群稀疏学习算法来提取故障脉冲,第一种仅利用故障脉冲的群稀疏性,第二种则利用故障脉冲的额外周期性行为(Matlab代码实现)事科研或工程应用的研究生、工程师及科研人员;对智能诊断、稀疏表示、贝叶斯学习感兴趣的技术人员。; 使用场景及目标:①应用于旋转机械(如轴承、齿轮箱)的早期故障检测与健康监测;②研究群稀疏性与周期性先验在信号分离中的建模方法;③复现SBL算法并拓展至其他故障特征提取任务;④结合所提供的网盘资源开展相关领域仿真研究与算法开发。; 阅读建议:建议结合Matlab代码逐行理解算法实现细节,重点关注群稀疏建模与周期性约束的数学表达;推荐对比两种算法的实验结果以深入理解其性能差异;同时可利用提供的网盘资源拓展学习其他仿真技术,提升综合科研能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值