java接口pending,EventBus系列『番外』——认真剖析 『PendingPostQueue』队列的实现思想...

概念

什么是队列

队列是一种数据结构,它支持 FIFO,尾部添加、头部删除(先进队列的元素先出队列

Java中的常用队列

在Java中提供了一个 BlockingQueue接口,通过实现这个接口来实现队列存储,我们常用的队列有

ArrayBlockingQueue它在内部维护一个数组,通过对数组元素的增删改查,实现了队列对元素FIFO(先进先出)进行的排序。

LinkedBlockingQueue 它在内存维护了一个Node节点,实现队列对元素FIFO(先进先出)进行排序。

知识流程铺垫

我们先回想一下将事件放入队列的过程,以threadMode = MAIN为例:

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {

switch (subscription.subscriberMethod.threadMode) {

...

case MAIN:

if (isMainThread) {

//直接反射执行

invokeSubscriber(subscription, event);

} else {

//入列操作

mainThreadPoster.enqueue(subscription, event);

}

break;

...

}

进入HandlerPoster.java,执行enqueue 函数,将Event放入队列中

public void enqueue(Subscription subscription, Object event) {

//将完整Event事件封装成 PendingPost 类型

PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);

synchronized (this) {

//入列

queue.enqueue(pendingPost);

if (!handlerActive) {

handlerActive = true;

if (!sendMessage(obtainMessage())) {

throw new EventBusException("Could not send handler message");

}

}

}

}

PendingPostQueue队列的实现不同于上述两个队列,它的内部既 没有维护 数组 ,也没有维护Node节点,同时也没有维护 List链表,那么它是如何实现的呢?

答案是:

我们先了解它内部维护的变量都有哪些 :

final class PendingPostQueue {

//头部

private PendingPost head;

//尾部

private PendingPost tail;

}

通过源码我们可以看到PendingPostQueue队列的头部和尾部都被包装成了PendingPost类型,我们来看看PendingPost类的实现:

final class PendingPost {

private final static List pendingPostPool = new ArrayList();

Object event;

Subscription subscription;

//内部变量,由于存储下一个PendingPost对象

PendingPost next;

private PendingPost(Object event, Subscription subscription) {

this.event = event;

this.subscription = subscription;

}

//@重点:在将Event事件放入队列之前,我们会执行此方法。

//将完整的Event事件包装成PendingPost对象

static PendingPost obtainPendingPost(Subscription subscription, Object event) {

synchronized (pendingPostPool) {

int size = pendingPostPool.size();

//判定pendingPostPool链表大小

if (size > 0) {

//获取并移除链表中的最后一个元素

PendingPost pendingPost = pendingPostPool.remove(size - 1);

//重新为最后一个元素赋值

pendingPost.event = event;

pendingPost.subscription = subscription;

pendingPost.next = null;

//返回一个包装好的 PendingPost 对象

return pendingPost;

}

}

//new 一个PendingPost对象 进行包装

return new PendingPost(event, subscription);

}

//将 pendingPost元素保存至链表,链表最大不超过 10000

static void releasePendingPost(PendingPost pendingPost) {

pendingPost.event = null;

pendingPost.subscription = null;

pendingPost.next = null;

synchronized (pendingPostPool) {

// Don't let the pool grow indefinitely

if (pendingPostPool.size() < 10000) {

pendingPostPool.add(pendingPost);

}

}

}

}

我们在 知识流程铺垫 可以看到放入队列之前先把完整Event事件封装成 PendingPost, 即仅执行 obtainPendingPost(Subscription subscription, Object event)函数。

我们看看是如何通过内存指针,指向下一个元素的内存位置,获取元素值的

进栈流程

final class PendingPostQueue {

private PendingPost head;

private PendingPost tail;

synchronized void enqueue(PendingPost pendingPost) {

//判定pendingPost是否为空

if (pendingPost == null) {

throw new NullPointerException("null cannot be enqueued");

}

//尾部不为空,证明队列中已存在其他元素

if (tail != null) {

tail.next = pendingPost; //为tail对象的 next属性赋值

tail = pendingPost; //重新为tail对象赋值

} else if (head == null) { //头部为空,证明队列是空的,进入元素是第一个

head = tail = pendingPost; //同时赋值给头尾

} else {

throw new IllegalStateException("Head present, but no tail");

}

notifyAll();

}

}

入栈时先进行判Null,以免Null进入堆栈.

若是堆栈首次插入元素 A,此时的 head 和 tail 都应该为Null,将需要插入元素A同时赋给 head 和 tail, 那么此时 head 和 tail的内存指针都是指向 A 的

3.若我们再次执行插入元素 B,此时的head 和 tail都是有值的,那么我们将执行 tail.next = B相当于将元素 B赋予了A的A.next属性值,那么此时 head.next的值也就等于B;紧接着我们重新为tail赋新值 B,那么此时 A.next 和 head.next 就等于 tail .

4.我们就如此一直插入数据,tail永远保存最后一个值,而通过不断遍历head.next我们就可以难道堆栈里所有的值.实现了FIFO(先进先出)排序原则

出栈流程

final class PendingPostQueue {

private PendingPost head;

private PendingPost tail;

//出栈

synchronized PendingPost poll() {

PendingPost pendingPost = head; //获取头部数据

if (head != null) { //若head不为空,证明队列中还存在数据

head = head.next; //获取下一个数据元素,赋予 head

if (head == null) { //若下一个数据元素为空,证明队列已无元素

tail = null; //将尾部设置为Null

}

}

return pendingPost; //返回pendingPost对象

}

//出栈,指定最长等待时间

synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {

if (head == null) {

wait(maxMillisToWait); //等待时长

}

return poll();

}

}

第一次执行poll函数,获取头部的pendingPost元素,然后进行判断head值是否为空,若不为Null,获取head.next值作为下一次的执行poll函数的head值,一次类推直到head.next为Null时,说明没有了下一元素,也就说到 栈底 了,将tail置Null,结束。

简单实例

为了方便了解,我做了一个依照EventBus的结构我做了一个Demo小样,供大家参考:

Entry.java

public class Entry {

private String flag;

Entry next;

public Entry(String flag) {

this.flag = flag;

}

public static Entry createEntry(String flag) {

return new Entry(flag);

}

}

EntryControl.java

public class EntryControl {

private Entry head;

private Entry tail;

synchronized void enqueue(Entry entry) {

if (tail != null) {

tail.next = entry;

tail = entry;

} else if (head == null) {

head = tail = entry;

} else {

throw new IllegalStateException("Head present, but no tail");

}

System.out.println("Head HashCode:" + head.hashCode());

System.out.println("Tail HashCode:" + tail.hashCode());

notifyAll();

}

synchronized Entry poll() {

Entry entry = head;

if (head != null) {

head = head.next;

if (head == null) {

tail = null;

}

System.out.println("Current Head HashCode:" + entry.hashCode());

}

return entry;

}

}

Test.java 执行程序

public class Test {

public static void main(String[] args) {

EntryControl entryControl = new EntryControl();

for (int index = 0; index < 5; index++) {

Entry entry = Entry.createEntry("我是第" + index);

entryControl.enqueue(entry);

}

System.out.println("存值完成===========================");

while (true) {

Entry pendingPost = entryControl.poll();

if (pendingPost == null) {

return;

}

// System.out.println().e(TAG,"我获取的记过" + pendingPost.subscription + "," + pendingPost.event);

}

}

}

运行结果

Head HashCode:356573597

Tail HashCode:356573597

Head HashCode:356573597

Tail HashCode:1735600054

Head HashCode:356573597

Tail HashCode:21685669

Head HashCode:356573597

Tail HashCode:2133927002

Head HashCode:356573597

Tail HashCode:1836019240

存值完成===========================

Current Head HashCode:356573597

Current Head HashCode:1735600054

Current Head HashCode:21685669

Current Head HashCode:2133927002

Current Head HashCode:1836019240

如此就清晰明了了.

This ALL! Thanks EveryBody!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值