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来存储具体的数据,达到高效存储的目的。