首页 > 代码库 > Handler、Thread和Runnable简单分析

Handler、Thread和Runnable简单分析

  Handler、Thread和Runnable在开发中频繁使用,很多新手都因为概念不清而头绪全无,在这我来简单得缕缕这三者的联系与区别。

  Runnable是最简单的,它并没有什么包装,Android源码如下:

 1 /** 2  * Represents a command that can be executed. Often used to run code in a 3  * different {@link Thread}. 4  */ 5 public interface Runnable { 6  7     /** 8      * Starts executing the active part of the class‘ code. This method is 9      * called when a thread is started that has been created with a class which10      * implements {@code Runnable}.11      */12     public void run();13 }

  Runnable就是一个非常简单的接口,注释上说的是“代表一个能被执行的命令,总是用来在新的线程中运行”。
  我们再来看看Runnable的子类Thread,我们经常使用Thread来新建一个线程脱离原线程来单独跑,也经常把Runnable的实现类用Thread来包装成线程的主要执行内容:Thread thread = new Thread(Runnable)。我们就先来屡屡Thread thread = new Thread(Runnable)的过程。

 1     /** 2      * Constructs a new {@code Thread} with a {@code Runnable} object and a 3      * newly generated name. The new {@code Thread} will belong to the same 4      * {@code ThreadGroup} as the {@code Thread} calling this constructor. 5      * 6      * @param runnable 7      *            a {@code Runnable} whose method <code>run</code> will be 8      *            executed by the new {@code Thread} 9      *10      * @see java.lang.ThreadGroup11      * @see java.lang.Runnable12      */13     public Thread(Runnable runnable) {14         create(null, runnable, null, 0);15     }

  注释说用Runnable来构造一个线程实例,且创建的线程属于相同的ThreadGroup(是一种线程容器,create方法的第一个参数代表这个),我们来看看create方法都做了什么。

/**     * Initializes a new, existing Thread object with a runnable object,     * the given name and belonging to the ThreadGroup passed as parameter.     * This is the method that the several public constructors delegate their     * work to.     *     * @param group ThreadGroup to which the new Thread will belong     * @param runnable a java.lang.Runnable whose method <code>run</code> will     *        be executed by the new Thread     * @param threadName Name for the Thread being created     * @param stackSize Platform dependent stack size     * @throws IllegalThreadStateException if <code>group.destroy()</code> has     *         already been done     * @see java.lang.ThreadGroup     * @see java.lang.Runnable     */    private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {        Thread currentThread = Thread.currentThread();        if (group == null) {            //前面我们说过了,用Runnable新建的线程和原线程属于同一线程容器            group = currentThread.getThreadGroup();              }        if (group.isDestroyed()) {            throw new IllegalThreadStateException("Group already destroyed");        }        this.group = group;        //此处用synchronized来保证新建的线程id+1并且使唯一的        synchronized (Thread.class) {            id = ++Thread.count;        }        //threadName         if (threadName == null) {            this.name = "Thread-" + id;        } else {            this.name = threadName;        }        //相当于目标任务        this.target = runnable;        //线程开辟栈的大小,为0就是默认值8M        this.stackSize = stackSize;        //新线程的优先级和父线程是一样的        this.priority = currentThread.getPriority();        this.contextClassLoader = currentThread.contextClassLoader;        // Transfer over InheritableThreadLocals.        if (currentThread.inheritableValues != null) {            inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues);        }             // add ourselves to our ThreadGroup of choice        this.group.addThread(this);    }

  在新建完线程之后,我们有两种方法来启动线程任务:thread.run() ; thread.start()。这两者有啥区别嘞?都是这么说的:

  thread.run() ,实际上只是在UI线程执行了Runnable的任务方法并没有实现多线程,系统也没有新开辟一个线程。

  thread.start(),才是新开一个多线程,并且在新开的线程执行Thread你们的run()方法。

  对于thread.run(),由简单的java继承机制也知道,它只是执行了Runnable的run方法,我们来看看源码吧!

 1     /** 2      * Calls the <code>run()</code> method of the Runnable object the receiver 3      * holds. If no Runnable is set, does nothing. 4      * 5      * @see Thread#start 6      */ 7     public void run() { 8         if (target != null) { 9             target.run();10         }11     }

  由上面的create方法我们知道target就是Runnable包装在thread中的实例,还没有做任何事情,我们知道线程的新建需要请求CPU,所以直接调用run方法确实没有新建线程,只是在currentThread中直接执行了一个方法而已。我们再来看看thread.start()方法的流程又是怎么样的。

 1     /** 2      * Starts the new Thread of execution. The <code>run()</code> method of 3      * the receiver will be called by the receiver Thread itself (and not the 4      * Thread calling <code>start()</code>). 5      * 6      * @throws IllegalThreadStateException - if this thread has already started. 7      * @see Thread#run 8      */ 9     public synchronized void start() {10         checkNotStarted();11 12         hasBeenStarted = true;13 14         nativeCreate(this, stackSize, daemon);15     }16 17     private native static void nativeCreate(Thread t, long stackSize, boolean daemon);

  方法同样用synchronized关键字修饰,用来防止同一个线程阻塞。而方法的执行交给了nativeCreate方法,并且把当前Thread的实例自己传了进去,而this中就我们所知的,带了这么几个参数:

 1     volatile ThreadGroup group; 2     volatile boolean daemon; 3     volatile String name; 4     volatile int priority; 5     volatile long stackSize; 6     Runnable target; 7     private static int count = 0; 8  9     /**10      * Holds the thread‘s ID. We simply count upwards, so11      * each Thread has a unique ID.12      */13     private long id;14 15     /**16      * Normal thread local values.17      */18     ThreadLocal.Values localValues;19 20     /**21      * Inheritable thread local values.22      */23     ThreadLocal.Values inheritableValues;

  那我们只好跟过去,看看在native中到底是怎么新建的新建的线程,资源又是如何请求的(新建线程的关键)。
  顺着nativeCreate方法,我们发现它换了方法名调用的/android/art/runtime/native/java_lang_Thread.cc里面方法,名字换成了CreateNativeThread:

 1 static JNINativeMethod gMethods[] = { 2   NATIVE_METHOD(Thread, currentThread, "!()Ljava/lang/Thread;"), 3   NATIVE_METHOD(Thread, interrupted, "!()Z"), 4   NATIVE_METHOD(Thread, isInterrupted, "!()Z"), 5   NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"), 6   NATIVE_METHOD(Thread, nativeGetStatus, "(Z)I"), 7   NATIVE_METHOD(Thread, nativeHoldsLock, "(Ljava/lang/Object;)Z"), 8   NATIVE_METHOD(Thread, nativeInterrupt, "!()V"), 9   NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"),10   NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"),11   NATIVE_METHOD(Thread, sleep, "!(Ljava/lang/Object;JI)V"),12   NATIVE_METHOD(Thread, yield, "()V"),13 };

  我们可以看到关于线程管理的一些方法全在里面,包括sleep、interrupted等,继续dig到CreateNativeThread的实现

 1 void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) { 2   CHECK(java_peer != nullptr);//即为java层的thread实例,包裹着run方法的具体实现 3   Thread* self = static_cast<JNIEnvExt*>(env)->self; 4   Runtime* runtime = Runtime::Current(); 5  6   // Atomically start the birth of the thread ensuring the runtime isn‘t shutting down. 7   bool thread_start_during_shutdown = false;//这段代码用来检测thread是否在runtime宕机时start的 8   { 9     MutexLock mu(self, *Locks::runtime_shutdown_lock_);10     if (runtime->IsShuttingDownLocked()) {11       thread_start_during_shutdown = true;12     } else {13       runtime->StartThreadBirth();14     }15   }16   if (thread_start_during_shutdown) {//若runtime宕机了就抛出异常17     ScopedLocalRef<jclass> error_class(env, env->FindClass("java/lang/InternalError"));18     env->ThrowNew(error_class.get(), "Thread starting during runtime shutdown");19     return;20   }21 22   Thread* child_thread = new Thread(is_daemon);//新建子线程23   // Use global JNI ref to hold peer live while child thread starts.24   child_thread->tlsPtr_.jpeer = env->NewGlobalRef(java_peer);//把java层的run方法实体传递给子线程25   stack_size = FixStackSize(stack_size);26 27   // Thread.start is synchronized, so we know that nativePeer is 0, and know that we‘re not racing to28   // assign it.29   env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer,30                     reinterpret_cast<jlong>(child_thread));31 32   pthread_t new_pthread;33   pthread_attr_t attr;34   CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), "new thread");35   CHECK_PTHREAD_CALL(pthread_attr_setdetachstate, (&attr, PTHREAD_CREATE_DETACHED), "PTHREAD_CREATE_DETACHED");36   CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), stack_size);37   //创建新线程的方法,返回一个标志38   int pthread_create_result = pthread_create(&new_pthread, &attr, Thread::CreateCallback, child_thread);39   CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread");40 41   //线程创建如果失败则清除子线程信息,释放空间42   if (pthread_create_result != 0) {43     // pthread_create(3) failed, so clean up.44     {45       MutexLock mu(self, *Locks::runtime_shutdown_lock_);46       runtime->EndThreadBirth();47     }48     // Manually delete the global reference since Thread::Init will not have been run.49     env->DeleteGlobalRef(child_thread->tlsPtr_.jpeer);50     child_thread->tlsPtr_.jpeer = nullptr;51     delete child_thread;52     child_thread = nullptr;53     // TODO: remove from thread group?54     env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0);55     {56       std::string msg(StringPrintf("pthread_create (%s stack) failed: %s",57                                    PrettySize(stack_size).c_str(), strerror(pthread_create_result)));58       ScopedObjectAccess soa(env);59       soa.Self()->ThrowOutOfMemoryError(msg.c_str());60     }61   }62 }

  创建新线程在pthread_create(&new_pthread, &attr, Thread::CreateCallback, child_thread)方法里由java Runtime实现,由于那里过于深入,我们就不往下挖了(往下我已经看不懂了)。但是我们有个一个创新线程过程的概念,进一步的了解了thread的id号,父线程等概念。也知道了只有通过thread.start()方法才会创建一个新的线程。
  说清了Thread和Runnable的关系,我们再来说说Handler,Handler是啥呢?当我们需要进行线程间通信的时候,我们就需要用到Handler,我们把Handler认为是一个维护消息循环队列的东西,书上都这么说,那么Handler是如何维护消息队列呢?要弄清楚这个还是得看源码。

  Google给出的关于Handler简介,大家感受下:

 1 /** 2  * A Handler allows you to send and process {@link Message} and Runnable 3  * objects associated with a thread‘s {@link MessageQueue}.  Each Handler 4  * instance is associated with a single thread and that thread‘s message 5  * queue.  When you create a new Handler, it is bound to the thread / 6  * message queue of the thread that is creating it -- from that point on, 7  * it will deliver messages and runnables to that message queue and execute 8  * them as they come out of the message queue. 9  * 10 **/

  大意是三点:

  1:Handler可以在线程的帮助下用来发送和处理Message和Runnable实例;

  2:每个Handler实例都是和单个线程和此线程的消息队列绑定的;

  3:Handler负责的工作是传送message到消息队列中且在它们从队列中出来的时候对它们进行处理。

  那么Handler是怎么维护消息队列呢?在处理消息过程中,我们常用到的几个方法:sendMessage、sendMessageAtFrontOfQueue、sendMessageAtTime、sendMessageDelayed 以及 handleMessage 。我们来看看这几个方法的源码:

 1 public final boolean sendMessage(Message msg) 2     { 3         return sendMessageDelayed(msg, 0); 4     } 5 public final boolean sendMessageDelayed(Message msg, long delayMillis) 6     { 7         if (delayMillis < 0) { 8             delayMillis = 0; 9         }10         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);11     }12 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {13         MessageQueue queue = mQueue;14         if (queue == null) {15             RuntimeException e = new RuntimeException(16                     this + " sendMessageAtTime() called with no mQueue");17             Log.w("Looper", e.getMessage(), e);18             return false;19         }20         return enqueueMessage(queue, msg, uptimeMillis);21     }22 public final boolean sendMessageAtFrontOfQueue(Message msg) {23         MessageQueue queue = mQueue;24         if (queue == null) {25             RuntimeException e = new RuntimeException(26                 this + " sendMessageAtTime() called with no mQueue");27             Log.w("Looper", e.getMessage(), e);28             return false;29         }30         return enqueueMessage(queue, msg, 0);31     }

  我们可以看到除了设置的不同,发送message的每个方法都实现了enqueueMessage()方法,我们看看enqueueMessage()的源码:

1     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {2         msg.target = this;3         if (mAsynchronous) {4             msg.setAsynchronous(true);5         }6         return queue.enqueueMessage(msg, uptimeMillis);7     }

  追到MessageQueue里面,我们可以看到一个经典的队列add的操作:

 1     boolean enqueueMessage(Message msg, long when) { 2         if (msg.target == null) { 3             throw new IllegalArgumentException("Message must have a target."); 4         } 5         if (msg.isInUse()) { 6             throw new IllegalStateException(msg + " This message is already in use."); 7         } 8  9         synchronized (this) {10             if (mQuitting) {11                 IllegalStateException e = new IllegalStateException(12                         msg.target + " sending message to a Handler on a dead thread");13                 Log.w("MessageQueue", e.getMessage(), e);14                 msg.recycle();15                 return false;16             }17 18             msg.markInUse();19             msg.when = when;20             Message p = mMessages;21             boolean needWake;22             if (p == null || when == 0 || when < p.when) {23                 // New head, wake up the event queue if blocked.24                 msg.next = p;25                 mMessages = msg;26                 needWake = mBlocked;27             } else {28                 // Inserted within the middle of the queue.  Usually we don‘t have to wake29                 // up the event queue unless there is a barrier at the head of the queue30                 // and the message is the earliest asynchronous message in the queue.31                 needWake = mBlocked && p.target == null && msg.isAsynchronous();32                 Message prev;33                 //经典的队列add操作34                 for (;;) {35                     prev = p;36                     p = p.next;37                     if (p == null || when < p.when) {38                         break;39                     }40                     if (needWake && p.isAsynchronous()) {41                         needWake = false;42                     }43                 }44                 msg.next = p; // invariant: p == prev.next45                 prev.next = msg;46             }47 48             // We can assume mPtr != 0 because mQuitting is false.49             if (needWake) {50                 nativeWake(mPtr);51             }52         }53         return true;54     }

  可以看到Handler每次sendmessage时其实都是一个把message实例加到MessageQueue中的过程,无论延时还是不延时。
  发送message到消息队列的过程我们清楚了,那么Handler是怎么从消息队列中往外拿message的呢?这时候就是Looper出场的时候了,Looper也维护着一个MessageQueue,没错,这个messagequeue就是Handler发送的那个,Looper的责任就是接收消息,另一方面不停地检查messagequeue里有没有message,如果有就调用Handler中的dispatchMessage方法进行处理,我们来看看源码是怎么写的。

 1     /** 2      * Run the message queue in this thread. Be sure to call 3      * {@link #quit()} to end the loop. 4      */ 5     public static void loop() { 6         final Looper me = myLooper(); 7         if (me == null) { 8             throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread."); 9         }10         final MessageQueue queue = me.mQueue;11 12         // Make sure the identity of this thread is that of the local process,13         // and keep track of what that identity token actually is.14         Binder.clearCallingIdentity();15         final long ident = Binder.clearCallingIdentity();16 17         for (;;) {18             Message msg = queue.next(); // might block19             if (msg == null) {20                 // No message indicates that the message queue is quitting.21                 return;22             }23 24             // This must be in a local variable, in case a UI event sets the logger25             Printer logging = me.mLogging;26             if (logging != null) {27                 logging.println(">>>>> Dispatching to " + msg.target + " " +28                         msg.callback + ": " + msg.what);29             }30 31             msg.target.dispatchMessage(msg);32 33             if (logging != null) {34                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);35             }36 37             // Make sure that during the course of dispatching the38             // identity of the thread wasn‘t corrupted.39             final long newIdent = Binder.clearCallingIdentity();40             if (ident != newIdent) {41                 Log.wtf(TAG, "Thread identity changed from 0x"42                         + Long.toHexString(ident) + " to 0x"43                         + Long.toHexString(newIdent) + " while dispatching to "44                         + msg.target.getClass().getName() + " "45                         + msg.callback + " what=" + msg.what);46             }47 48             msg.recycleUnchecked();49         }50     }

  我们能看到有一句处理消息的代码: msg.target.dispatchMessage(msg);

  再看看enqueueMessage方法里:msg.target = this;

  对的,message的target就是handler:

 1    /** 2      * Handle system messages here. 3      */ 4     public void dispatchMessage(Message msg) { 5         if (msg.callback != null) { 6             handleCallback(msg); 7         } else { 8             if (mCallback != null) { 9                 if (mCallback.handleMessage(msg)) {10                     return;11                 }12             }13             handleMessage(msg);14         }15     }

  一切的一切都回到了handleMessage()方法中,我们最熟悉的方法。
  我们用一张流程图来总结一下Handler、Thread和Runnable三者的关系。

Handler、Thread和Runnable简单分析