akka一些邮箱的实现

private[akka] abstract class Mailbox(val messageQueue: MessageQueue)
  extends SystemMessageQueue with Runnable

Mailbox的实现主要还是依靠各种消息队列来实现各种要求。

def enqueue(receiver: ActorRef, msg: Envelope): Unit = messageQueue.enqueue(receiver, msg)

/**
 * Try to dequeue the next message from this queue, return null failing that.
 */
def dequeue(): Envelope = messageQueue.dequeue()

/**
 * Indicates whether this queue is non-empty.
 */
def hasMessages: Boolean = messageQueue.hasMessages

/**
 * Should return the current number of messages held in this queue; may
 * always return 0 if no other value is available efficiently. Do not use
 * this for testing for presence of messages, use `hasMessages` instead.
 */
def numberOfMessages: Int = messageQueue.numberOfMessages

关于消息的接受获取都是通过MessageQueue来实现的。

class UnboundedPriorityMailbox(val cmp: Comparator[Envelope], val initialCapacity: Int)
  extends MailboxType with ProducesMessageQueue[UnboundedPriorityMailbox.MessageQueue] {
  def this(cmp: Comparator[Envelope]) = this(cmp, 11)
  final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
    new UnboundedPriorityMailbox.MessageQueue(initialCapacity, cmp)
}

object UnboundedPriorityMailbox {
  class MessageQueue(initialCapacity: Int, cmp: Comparator[Envelope])
    extends PriorityBlockingQueue[Envelope](initialCapacity, cmp) with UnboundedQueueBasedMessageQueue {
    final def queue: Queue[Envelope] = this
  }
}

UnboundedPriorityMailbox通过PriorityBlockingQueue来实现带有优先级的无界消息队列,来实现存在优先级的无界邮箱。PriorityBlockingQueue通过大根堆来实现优先级消息的优先消费。

而UnboundedStablePriorityMailbox则提供了fifo的优先级无界消息队列实现,在原来UnboundedPriorityMailbox的基础上,增加了PriorityQueueStabilizer的实现,在原本优先级的比较上,再次根据原本消息进入队列的下标进行比较,达到同优先级fifo的目的。

object PriorityQueueStabilizer {
  class WrappedElement[E](val element: E, val seqNum: Long)
  class WrappedElementComparator[E](val cmp: Comparator[E]) extends Comparator[WrappedElement[E]] {
    def compare(e1: WrappedElement[E], e2: WrappedElement[E]): Int = {
      val baseComparison = cmp.compare(e1.element, e2.element)
      if (baseComparison != 0) baseComparison
      else {
        val diff = e1.seqNum - e2.seqNum
        java.lang.Long.signum(diff)
      }
    }
  }
}

NonBlockingBoundedMailbox则提供了高效的多生产者单消费者有界邮箱,主要依靠BoundedNodeMessageQueue来实现,其主要实现依靠其继承的AbstractBoundedNodeQueue。

AbstractBoundedNodeQueue中的具体消息被其抽象为其内部类Node。

public static class Node<T> {
    protected T value;
    @SuppressWarnings("unused")
    private volatile Node<T> _nextDoNotCallMeDirectly;
    protected int count;

    @SuppressWarnings("unchecked")
    public final Node<T> next() {
        return (Node<T>)Unsafe.instance.getObjectVolatile(this, nextOffset);
    }

    protected final void setNext(final Node<T> newNext) {
      Unsafe.instance.putOrderedObject(this, nextOffset, newNext);
    }
    
    private final static long nextOffset;
    
    static {
        try {
            nextOffset = Unsafe.instance.objectFieldOffset(Node.class.getDeclaredField("_nextDoNotCallMeDirectly"));
        } catch(Throwable t){
            throw new ExceptionInInitializerError(t);
        } 
    }
} 

其中value为具体的消息值,count则为消息进入队列的序号,而_DoNotCallMeDirectly则为队列中的下一个消息node,但正如其名字,不会主动去获取,而是通过unsafe获取其偏移量nextOffset,当需要获取的时候直接通过unsafe来得到所需要的下一条消息。

 

同样的,AbstractBoundedNodeQueue也不会直接通过对象引用来获取队首和队尾的值,也是通过偏移量的方式通过unsafe来获取。

static {
    try {
      enqOffset = Unsafe.instance.objectFieldOffset(AbstractBoundedNodeQueue.class.getDeclaredField("_enqDoNotCallMeDirectly"));
      deqOffset = Unsafe.instance.objectFieldOffset(AbstractBoundedNodeQueue.class.getDeclaredField("_deqDoNotCallMeDirectly"));
    } catch(Throwable t){
        throw new ExceptionInInitializerError(t);
    }
}

看到其add()方法。

public final boolean add(final T value) {
    for(Node<T> n = null;;) {
        final Node<T> lastNode = getEnq();
        final int lastNodeCount = lastNode.count;
        if (lastNodeCount - getDeq().count < capacity) {
          // Trade a branch for avoiding to create a new node if full,
          // and to avoid creating multiple nodes on write conflict á la Be Kind to Your GC
          if (n == null) {
              n = new Node<T>();
              n.value = value;
          }

          n.count = lastNodeCount + 1; // Piggyback on the HB-edge between getEnq() and casEnq()

          // Try to append the node to the end, if we fail we continue loopin'
          if(casEnq(lastNode, n)) {
              lastNode.setNext(n);
              return true;
          }
        } else return false; // Over capacity—couldn't add the node
    }
}

首先,获取队尾node,根据和队首node之间count的差值来获取当前队列内的消息个数是否已经大于容量如果没有,新建一个Node准备将消息放入,通过cas的方式修改当前队尾元素的nextOffset来存储具体的数据,达到高效存储的目的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值