Android Handler线程通信机制详解及手动实现

闲扯

        -很多类比如AsynTask之类已经对Handler实现了封装,我不用了解Handler的机制,我也可以很快乐,那我还用了解handler吗?
        -了解了Handler,你可以更快乐。

        -不是有那啥EventBus很屌吗,老东西你的Handler最没用了。
        -没有最强的code,只有最强的coder。解耦不一定就好过耦合,Handler仍然不可替代。

概述

        HandlerLooperMessageQueueThreadLocaL四个组件共同实现了android的线程间通信。为了方便理解整个通信机制,我们可以打个比方:
        Child Thread是一个淘宝网店,它要把一个快递Message寄给买家HandlerHandler住在一个小区,小区叫UI Thread。快递公司根据地址确定了你所在地的蜂鸟仓库MessageQueue,并把快递存放进去。快递存在仓库中了,这时候,承包UI Thread小区快递生意的快递小哥Looper就会更新一下Handler淘宝上的快递信息,说你的快递已经到了XX仓库,麻烦来拿一下。然后Handler就拿到了自己想要的Message
在这里插入图片描述
        首先是一个小区不会限制网购物品的人只能有一个,所以一个线程也不会限制Handler的数量;其次,小区Thread才不会分自己是买家小区还是卖家小区,买家和卖家都可以住在同一个小区,所以线程也不会以发送消息的线程,接受消息的线程去区分,所有线程里都可以有Handler,但有条件;然后,这个条件也是相同的,为了让买家能够收到快递,必须要有快递小哥的帮助,如果没有快递小哥,说明该小区不在我们的服务范围,不送的,既然收不到快递,那这个小区肯定没有买家,所以,一个线程里能能不能有Handler取决于这个线程有没有Looper实例。最后,一个小区一个快递小哥就够,多了抢生意,也不方便买家Handler去取件,而且小哥只有能力去管一个仓库MessageQueue,否则就忙不过来了,就是说每个Thread只能最多一个Looper,一个Looper必负责一个MessageQueue
        虽然在细节上还有点细微区别(比如下单的这个操作,大多数情况下不会涉及线程通信,因为相对于子线程,或者说是消息发送者,主线程,或说是消息接收者是其上帝,造物主。其一生的命运无法被改变,那就是发消息,发完,就被kill在这个例子中就是说卖家小区是买家小区建的,建的时候就已经命令卖家只能干什么了,相当于工厂?);偶尔出问题,罢工,上帝没拿到他想拿的东西,很生气,于是kill了它,重新创建了个线程)。
        接下来我们通过源码来认识一下真正的Handler机制。

Handler

        在说Handler的具体内容前,我们先想想它的作用:一是提供给消息发送者,告诉他消息传递的目标(对应于Message类中的target成员变量);二是通过一个接口成员变量mCallback的dispatchMessage方法来回调获取消息。这些功能由那些函数来实现呢?我们翻翻源码,发现了一大堆sendXXMessageXXX()和post(XXX xxx)方法用来发送信息,我们稍微理一理调用关系就可以发现,实际上所有的消息发送,都归到了一个方法身上

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

这十分容易理解:我不管你这个消息要干嘛,有什么要求,我的最终目的都是要把消息送出去,消息要送出去,那就传给指定的消息队列就好了啊,不管什么sendXXMessageXXX()还是post(XXX xxx)无非也就是在传递消息前对消息进行了一些加工,或者是我这个消息其实是个Runnable,但它本质还是消息,并不能阻止消息会被加入消息队列的真实。
        其次是接受消息,同样如同发送消息,但有细微区别,所有的方法最后都调用了

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

你看到,欸,这不全最后全是handleMessage吗,回归根源后不是handleMessage才想到与enqueueMessage的地位吗?我把获取消息的根源给dispatchMessage不是因为它到头了,而是因为到这里(倒数第二步)分叉了。表面看上去都是handleMessage,但稍稍认真看一下就可以发现,其中一个是接口的回调方法,一个是直接调用的函数,大哥是你自己继承Handler.Callback的接口函数,二哥是Handler.Callback自带的接口函数,有大哥的时候大哥顶住,没大哥二哥顶,大哥二哥都没有,那找臭鱼烂虾没接口的handleMessage也得顶住

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}

你看官方文档也说的很明白了,没有大哥二哥无所谓,但连臭鱼烂虾都没有就根本顶不住。
        Handler的功能讲的差不多了,我们再回过头看看构造方法。首先,我们知道发送消息必须要MessageQueue,接受消息要通过Looper,Looper的内部包含了这个MessageQueue,那我们构造Handler的时候,一个Looper是必要的,但实际上我们很多时候使用Handler,并未去指定Looper,那是因为不指定Looper时,Handler会自动获取当前线程Looper,UI线程自带Looper,所以不用传;其次,在接受消息的时候想自己定制一个大哥来帮我做事,好,那就自己写个Callback传给Handler,觉得二哥或者臭鱼烂虾够用了,那就不需要这个Callback,所以这个Callback是可选的;最后,是一个关于Message的布尔变量,由于设定了默认值所以一样可有可无(这涉及MessageQueue的入队机制,在此先不多说,我们在MessageQueue部分来详细介绍)。
        那么构造方法按理接应该有2^3=8种,实际也差不多是这样,一种以Looper和布尔变量为参的构造函数变了一下型,问题不大。

@NonNull
public static Handler createAsync(@NonNull Looper looper) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    return new Handler(looper, null, true);
}

Message与MessageQueue

Message

        单个Message实例很简单,容器而已,没什么好说的,它的复杂性体现在许多Message回收机制及MessageQueue。唯独注意一下

/**
*While the constructor of Message is public, the best way to get
* one of these is to call {@link #obtain Message.obtain()} or one of the
* {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
* them from a pool of recycled objects.
* /
public Message() {}
public static Message obtain() {...}

        这样的好处是每次不需要新创建一个Message实例,而去消息池中拿一个不用的来代替,这样可以减少申请内存的开销。另外还有一组整型成员变量及其方法

/** If set message is asynchronous */
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;//左移一位2

/*package*/ int flags;

public boolean isAsynchronous() {
    return (flags & FLAG_ASYNCHRONOUS) != 0;
}

public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;//位或,flags值为
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;//位并
    }
}

这说明Messae不仅仅是只有同步的(就是说,接收消息的线程接受不到消息,那我就一直堵着,消息来了在开工),也可以异步(先自己做点其他事,消息来了,再回头做这件事)。关于Handler机制的文章很多,但关于这方面都基本没有涉及,我会在MessageQueue中讲解这部分(因为这涉及Handler的第三个构造参数)。
        为了补充上面的内容,现在先来说说MessageQueue。先是一些基本的概念:首先MessageQueue是一个链表,一个链表就注定有节点,在MessageQueue中节点就是一个一个的Message,单个的Message已经构成节点,没有在新建一个节点类;

public class Message{
    ...
    public Message next;
}

其次,上文提到了,我们在实例化Message时,一般不会去new一个,而是从消息池中拿一个不用的——那么消息池怎么来的呢?我们先看看obtain的源码

public static Message obtain() {
    synchronized (sPoolSync) {//防止多个线程同时操作消息池
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

同步锁的这段代码,明显的,我们可以发现,消息池中,消息仍以链表形式存在,而且头删法获取元素。在消息池不为空时,我们从消息池中拿,当消息池为空时,我们直接构造一个新的,但是,这个消息池不是天上掉下来的吧,我们需要去找往消息池中添加消息的方法——recycleUnchecked()

void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        /**
        *为什么要把放在消息池里的消息标记为正在使用?是因为改方法被recycle()方法封装,recycle()中
        *对该值进行了检验,IN_USE的Message不会被回收,这样保证了Message不会被重复回收。(被标记为IN_USE的
        *Message可能在消息池中,可能在MessageQueue中,也可能马上回加入MessageQueue。)
        */
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;
        synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }//头插法向消息池中不停的添加消息
    }
}

recycleUnchecked()方法会在Message使用结束后调用。整个Message创建过程就是在挤牙膏,缺多少就挤多少,能够极大的节约空间。

Message的种类

        在消息队列中,我们把消息分为同步消息,异步消息和消息屏障。同步异步消息通过flags来进行区分,下面简单说说他们的区别。首先在没有消息屏障时,这两种消息在消息队列中没有区别,当消息屏障出现时区别得以体现。简单的来说,消息屏障就是一个target属性为null的消息,当消息屏障处于消息队列的队首时,当前消息队列就不再接收同步消息,且消息队列中已有的所有同步消息均不执行,当有异步消息enqueue时,可以优先于队列中所有的同步消息执行。

MessageQueue

        然后我们在来看MessageQueue,MessageQueue的许多方法实现在native层,对于native层的具体逻辑,对于整个Handler机制,没有必要去深究,我们只需要知道native层帮助我们完成了什么功能就OK。首先,native层帮我们完成了对MessageQueue的实例化,MessageQueue调用nativeInit()方法,该方法放回一个long和MessageQueue指针。

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

且由于MessageQueue是在native层构造的,为了避免native层的调用,导致我们无法回收该部分内存以致OOM,native层提供了nativeDestroy()方法

@Override
protected void finalize() throws Throwable {
    try {
        dispose();
    } finally {
        super.finalize();
    }
}

        MessageQueue中消息发送者想消息队列中发送消息,接受者从队列中取出消息,其本质也是一个生产者-消费者模型,所以必然会存在阻塞与唤醒,但由于上文提到的消息屏障的问题,我们同时需要考虑消息屏障对于阻塞的影响。enqueueMessage()和next()源码如下(分析见注释)

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
        //不能通过enqueueMessage的方式来添加消息屏障,若要手动添加消息屏障,需反射MessageQueue,调用postSyncBarrier(Long when)方法
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
        //在消息队列或消息池中的消息,不能入队
    }

    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }//目标线程已经退出,没有消息可送

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;//mMessage是整个队列的头结点
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {//三个判断条件分别代表:1.队列现在为空;2.该消息的等待时间为零;3.该消息的等待时间小于头结点消息等待时间,这时我们把新消息作为队列的头结点
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);//用来叫醒接收线程
        }
    }
    return true;
}

Message next() {
    // Return here if the message loop has already quit and been disposed.
    // This can happen if the application tries to restart a looper after quit
    // which is not supported.
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }

            // If first time idle, then get the number of idlers to run.
            // Idle handles only run if the queue is empty or if the first message
            // in the queue (possibly a barrier) is due to be handled in the future.
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }

        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;

        // While calling an idle handler, a new message could have been delivered
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;
    }
}

通过enqueueMessage()和next(),我们可以更加清楚的认识同步,异步消息与消息屏障的入队机制。

IdleHandler

        MessageQueue里的一个接口,和整个消息队列没有什么太大关系,简单介绍一下(我也不是很会用(-_-))。IdleHandler主要作用是让Handler在空闲的时候(当前MessageQueue中没有消息等待处理),有点事情可以做(回调函数,通过设置其返回值T或F来设置其一直执行或是执行一次后退出)。

Looper

      首先分析Looper接收消息的一些特性:
                1.Looper需要访问消息队列来获取消息;
                2.消息的数量与MessageQueue没有关系,只有消息发送者才知道自己要发多少条消息,Looper与发送者构成消费者-生产者的关系;
                3.消息的发送可能有延迟;
      在考虑我们对Looper的要求,保证每一条消息都可以准确的接收,那么Looper的设计就必须满足以下功能:
                1.能够访问消息队列,更加具体的来说是可以调用MessageQueue.next();
                2.由于需要不断读消息,且不知道消息的具体数量,同时需要根据生产队列的具体情况阻塞自己,唤醒生产者;反之,当消息队列中存在消息时,也同时可以唤醒Looper。
      为了满足这样的功能,首先我们需要写一个循环,去不停的去消息队列的数据,可是不知道具体要去多少个消息后该结束,不过我们知道如果没有消息可取,那应该等生产者生产消息;同时,当生产者发出消息时,Looper要被唤醒,继续循环。那么实际Looper的工作状态就是,循环——阻塞——循环——阻塞······,那么答案就很明显了,Looper就是个一直循环的东西。这样一通分析,其实Looper的loop()方法大概我们也分析完了,同时,为什么Looper是个死循环,却并不会有问题的原因也解释了。(说的书面一点是Looper在循环中由于阻塞会让出自己的CPU时间)

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    // Allow overriding a threshold with a system prop. e.g.
    // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
    final int thresholdOverride =
            SystemProperties.getInt("log.looper."
                    + Process.myUid() + "."
                    + Thread.currentThread().getName()
                    + ".slow", 0);

    boolean slowDeliveryDetected = false;

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long traceTag = me.mTraceTag;
        long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
        if (thresholdOverride > 0) {
            slowDispatchThresholdMs = thresholdOverride;
            slowDeliveryThresholdMs = thresholdOverride;
        }
        final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
        final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

        final boolean needStartTime = logSlowDelivery || logSlowDispatch;
        final boolean needEndTime = logSlowDispatch;

        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }

        final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
        final long dispatchEnd;
        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (logSlowDelivery) {
            if (slowDeliveryDetected) {
                if ((dispatchStart - msg.when) <= 10) {
                    Slog.w(TAG, "Drained");
                    slowDeliveryDetected = false;
                }
            } else {
                if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                        msg)) {
                    // Once we write a slow delivery log, suppress until the queue drains.
                    slowDeliveryDetected = true;
                }
            }
        }
        if (logSlowDispatch) {
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

      但是还有问题,是关于Looper如何保证接收到的消息准确的。这就要从Looper的构造方法说起

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

首先构造方法私有,只能通过prepare来实例化;其次,MessageQueue由Looper创建;最后,一个线程只能有一个Looper。ThreadLocal在Looper中的主要目的是保证Looper单线程单例(若采用单例模式就需要加锁,这个太影响效率了,单例加锁相当于时间换空间,ThreadLocal相当于空间换时间)(为什么设计Looper时需要单线程单例?自己实现Looper的时候不加ThreadLocal,多建立几个Looper试试看),至于其本身的一些机制,这里不细讲。

总结

      就提一下一些重点吧:
            1.只要是线程,都可以建Looper;只要有Looper的线程,都可以用Handler;
            2.一个线程,一个Looper,一个MessageQueue,n个Handler;
            3.通过设置消息屏障,优先异步消息;
            4.native层,MessageQueue通过epoll多路I/O复用来进行线程通信。

自己实现一个Handler

      通过一定的理解,我们可以自己实现一个简单的handler线程通信(也就单单模拟了一下通信功能,单个组件的功能并不完善),具体代码如下

Handler

public class Handler {
    private final Looper looper;
    private MessageQueue mQueue;

    public Looper getLooper() {
        return this.looper;
    }    

    public Handler(Looper looper) {
        this.looper = looper;
        this.mQueue = looper.mQueue;
    }

    public void sendMesssage(Message msg, long time){
        MessageQueue queue = mQueue;
        enqueueMessage(queue,msg,time);
    }

    private void enqueueMessage(MessageQueue queue,Message message,long time){
        message.target = this;
        queue.enqueueMessage(message,time);
    }

    public void dispatchMessage(Message message){
        handleMessage(message);
    }

    private void handleMessage(Message message){
    }
}

Looper

public class Looper {
    private static ThreadLocal<Looper> threadLocal = new ThreadLocal<>();
    public MessageQueue mQueue;

    public Looper() {
        this.mQueue = new MessageQueue();
    }

    public static Looper myLooper(){
        return threadLocal.get();
    }

    public static void prepare(){
        Looper looper = myLooper();
        if (looper == null) {
            threadLocal.set(new Looper());
        } else {
            throw new RuntimeException("Looper has been prepared in the current thread.");
        }
    }

    public static void loop(){
        Looper looper = threadLocal.get();
        if (looper == null) {
            throw new RuntimeException("Have you call the method prepare() before?");
        }
        MessageQueue messageQueue = looper.mQueue;
        while (true){
            Message msg = messageQueue.next();
            if (msg == null) {
                continue;
            } else {
                msg.target.dispatchMessage(msg);
            }
        }
    }
}

Message

public class Message {
    public int what;
    public Object obj;
    public Handler target;
    public long time;
}

MessageQueue

public class MessageQueue {

    Lock lock;
    Condition targetThread;
    Condition messageProduceThreads;
    private int MAX_INDEX = 10;//调整队列最大长度
    private LinkedList<Message> list = new LinkedList<>();
    /**
    *选用LinkedList来表示队列,只是因为懒,不想去重新写,两者差距还是很大的
    *且LinkedList不支持insert,所以时间参数虽然写了,但未体现出异步消息的特点
    */
   public MessageQueue() {
        lock = new ReentrantLock();
        targetThread = lock.newCondition();
        messageProduceThreads = lock.newCondition();
    }

    public  Message next() {
        Message message = new Message();
        try{
            lock.lock();
            while(list.size() == 0) {
               targetThread.await();
            }
            message = list.getFirst();
            list.removeFirst();
            messageProduceThreads.signalAll();
        } catch (InterruptedException e){
                e.printStackTrace();
        } finally {
            lock.unlock();
            return message;
        }
    }

    public void enqueueMessage(Message message, long time) {
        message.time = time;
        try {
            lock.lock();
            while (list.size() == MAX_INDEX){
                messageProduceThreads.await();
            }
            list.add(message);
            targetThread.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

使用

public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();
            Handler handler = new Handler(Looper.myLooper()){
                @Override
                public void dispatchMessage(Message message) {
                    System.out.println("handler" + "我get到了"+ String.valueOf(message.what) + "---" + String.valueOf(message.obj));
                }
            };
            //自己测试时对线程数量进行随意调整
            for (int i = 0;i<30;i++){
                final int num = i;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message msg = new Message();
                        msg.what = num;
                        msg.obj = Thread.currentThread().getId();
                        handler.sendMesssage(msg,System.currentTimeMillis());
                    }
                }).start();
            }
            Looper.loop();
        }
    }).start();
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值