深入研究Android Handler机制

http://www.xuebuyuan.com/705712.html

深入研究Android Handler机制

项目终于没那么忙了!闲下来几天,想想应该学点什么,总结点什么。总体上来,要学的东西实在太多了,看了看自己写的代码,结果发现连最基本的消息机制都没有了解清楚,虽然一直在用Handler发消息(Message),但一直没有去探究它们内部是如何运作的。于是花了一天的时间仔细分析了一下几个基本类的源码,略有所悟,浅析一下。

相关概念

在看源码前,我们先需要熟悉一下它们的概念及作用。
Message:用于封装消息的简单数据结构。里面包含消息的ID、数据对象、处理消息的Handler引用和Runnable等。
Handler:消息的发送者和最终消息处理者。
MessageQueue:消息队列,提供消息的添加、删除、获取等操作来管理消息队列。
Looper:用于建立消息循环并管理消息队列(MessageQueue),不停的从消息队列中抽取消息,分发下去并执行。

注:以下分析均以 android 2.3.3 源码为基础。

Message源码分析 

成员变量

我们先看一下它的成员变量。

[java] 
view plain
copy
  1. public final class Message implements Parcelable {  
  2.     public int what;  
  3.     public int arg1;   
  4.     public int arg2;  
  5.     public Object obj;  
  6.     public Messenger replyTo;  
  7.     /*package*/ long when;  
  8.     /*package*/ Bundle data;  
  9.     /*package*/ Handler target;       
  10.     /*package*/ Runnable callback;     
  11.     // sometimes we store linked lists of these things  
  12.     /*package*/ Message next;  
  13.     ......  
  14. }  

简单参数就不需要解释了,重点在以下几个成员变量。
long when:该消息何时被处理的绝对时间戳。
Handler target:谁来处理该消息。如果它为空,那说明该消息可能被recycle掉了,存放在Message Pool中,或者,它代表一个QUIT消息。
Runnable callback:Runnable对象,如果为该Message设置了该对象,那么有优先执行它。这里需要看Handler的消息处理机制。在分析Handler时再提。
Message next:这个看起来有点奇怪,有种似曾相识的感觉,想想,到底什么情况。哦,想起来了,就是C语言里面链表的数据结构。

[cpp] 
view plain
copy
  1. typedef struct LNode{  
  2.     ElemType data;  
  3.     Struct LNode* next;  
  4. }  

由于JAVA是没有指针这个概念的,所以内部维护了一个next的引用。所以,实际上,Message本身不单纯是一个简单的只包含数据的类,它实际上是一个链式结构的类,也就是说,一个Message本身就是一个消息队列,它通过next将所有消息串联起来。既然Message本身就是消息队列,那MessageQueue又是如何建立消息队列的又是怎么回事?实际上,MessageQueue内部只有一个Message成员,它所要做的工作就是把Message实体串连起来,形成消息链。

接着再看静态成员变量:

[java] 
view plain
copy
  1. private static Object mPoolSync = new Object(); // 用于访问mPool时进行同步操作  
  2. private static Message mPool;                   // 全局的废弃消息池(链)(就是废品收购站)  
  3. private static int mPoolSize = 0;               // 消息池当前大小  
  4. private static final int MAX_POOL_SIZE = 10;    // 消息池上限值  

从这上面能看出,有个叫mPool的Message对象,如果理解了Message本身就是链表结构,那么,应该就明白了为什么一个消息叫Pool(池),因为一个Message本身就代表着一群Message,通过next把一系列Message给串联起来。对于无数个message实体来说,他们共享同一个全局的消息池(链),里面存放废弃掉的message。很明显,这是在做缓存机制。

在该类中,核心函数有:

[java] 
view plain
copy
  1. public static Message obtain()及系列函数  
  2. public void recycle()  
  3. public void sendToTarget()  
obtain()及其系列函数

obtain()系列函数最核心的函数就只有obtain()方法,其它函数只不过提供了更多的可选参数,内部都是调用obtain()方法,因此,我们只需要关注核心函数的实现即可。

[java] 
view plain
copy
  1. /** 
  2.  * Return a new Message instance from the global pool. Allows us to 
  3.  * avoid allocating new objects in many cases. 
  4.  */  
  5. public static Message obtain() {  
  6.     synchronized (mPoolSync) {  
  7.         if (mPool != null) {  
  8.             Message m = mPool;  
  9.             mPool = m.next;  
  10.             m.next = null;  
  11.             return m;  
  12.         }  
  13.     }  
  14.     return new Message();  
  15. }  

该函数内部首先是从全局的废弃消息池(链)中去取,看看有没有废弃掉的Message,如果有,那我们就获取消息链中第一个废弃掉的Message,这样,就无需再创建一个新的Message;如果消息池中没有,那就只能new一个新的消息出来。这样做的好处就是废物再利用,减少创建时间。实际上,这种思想很值得我们借鉴。对于其它重载版的obtain方法,内部都是先调用它,然后再使用其它额外的参数进行填充的。如:

[java] 
view plain
copy
  1. public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {  
  2.         Message m = obtain();  
  3.         m.target = h;  
  4.         m.what = what;  
  5.         m.arg1 = arg1;  
  6.         m.arg2 = arg2;  
  7.         m.obj = obj;  
  8.   
  9.         return m;  
  10.     }  
recycle()函数
[java] 
view plain
copy
  1. /** 
  2.  * Return a Message instance to the global pool.  You MUST NOT touch 
  3.  * the Message after calling this function -- it has effectively been 
  4.  * freed. 
  5.  */  
  6. public void recycle() {  
  7.     synchronized (mPoolSync) {  
  8.         if (mPoolSize < MAX_POOL_SIZE) {  
  9.             clearForRecycle();  
  10.               
  11.             next = mPool;  
  12.             mPool = this;  
  13.         }  
  14.     }  
  15. }  

其中:clearForRecycle代码如下

[java] 
view plain
copy
  1. /*package*/ void clearForRecycle() {  
  2.     what = 0;  
  3.     arg1 = 0;  
  4.     arg2 = 0;  
  5.     obj = null;  
  6.     replyTo = null;  
  7.     when = 0;  
  8.     target = null;  
  9.     callback = null;  
  10.     data = null;  
  11. }  

这个函数首先是看当前消息池中废弃个数已达上限(池子是不是满了),如果没有达到上限,则调用clearForRecycle()函数把当前消息的各种信息清空,然后添加到消息链的头部。注意:该函数的if (mPoolSize < MAX_POOL_SIZE)实际上是没有起到任何作用的,搜遍Message所有代码也没有发现mPoolSize的值有任何变化,始终为0,也就是说,这句话是恒成立的。只要该Message被recycle掉,那他就会加入到废弃链中。

可以用以下图示表示该过程:

值得说明一点的是,该recycle()函数何时被调用?有以下两个时机被调用:

  • MessageQueue类中的removeMessages(...)及其系列函数,即当我们要从消息队列中干掉一个Message时,该Message被回收到废弃消息链。

[java] 
view plain
copy
  1. final boolean removeMessages(Handler h, int what, Object object, boolean doRemove) {    
  2.     synchronized (this) {    
  3.         Message p = mMessages;    
  4.         boolean found = false;    
  5.     
  6.         // Remove all messages at front.    
  7.         while (p != null && p.target == h && p.what == what    
  8.                && (object == null || p.obj == object)) {    
  9.             if (!doRemove) return true;    
  10.             found = true;    
  11.             Message n = p.next;    
  12.             mMessages = n;    
  13.             p.recycle();    
  14.             p = n;    
  15.         }    
  16.         // Remove all messages after front   
  17.         while (p != null) {    
  18.             Message n = p.next;    
  19.             if (n != null) {    
  20.                 if (n.target == h && n.what == what    
  21.                     && (object == null || n.obj == object)) {    
  22.                     if (!doRemove) return true;    
  23.                     found = true;    
  24.                     Message nn = n.next;    
  25.                     n.recycle();    
  26.                     p.next = nn;    
  27.                     continue;    
  28.                 }    
  29.             }    
  30.             p = n;    
  31.         }    
  32.             
  33.         return found;    
  34.     }    
  35. }    

  • Looper类中的loop函数。即当我们使用完了某个Message后,该Message被回收到废弃消息链。

[java] 
view plain
copy
  1. public static final void loop() {    
  2.     Looper me = myLooper();    
  3.     MessageQueue queue = me.mQueue;    
  4.     while (true) {    
  5.         Message msg = queue.next(); // might block    
  6.         if (msg != null) {    
  7.             if (msg.target == null) {    
  8.                 // No target is a magic identifier for the quit message.    
  9.                 return;    
  10.             }    
  11.             msg.target.dispatchMessage(msg);    
  12.             msg.recycle();    
  13.         }    
  14.     }    
  15. }    
sendToTarget()函数
[java] 
view plain
copy
  1. public void sendToTarget() {  
  2.     target.sendMessage(this);  
  3. }  

该函数比较简单,就是通过Message内部引用的Handler将消息发送出去。

Handler源码分析

成员变量

我们先看一下它的成员变量:

[java] 
view plain
copy
  1. final MessageQueue mQueue;  
  2. final Looper mLooper;  
  3. final Callback mCallback;  

Handler作为一个管理者,其重要做用就是创建并发送消息,最后再处理消息。
发送消息即为把指定的Message放入到消息队列中,等到合适的时机,消息泵从消息队列中抽取消息,再分发下去,进行处理。
因此,在Handler中,有必要维护当前线程的MessageQueue和Looper的引用。对于一个线程来说,MessageQueue和Looper都是唯一的,而多个handler是可以共享同一个线程的MessageQueue和Looper的引用。

Handler里面有以下几类核心函数共同完成上面的功能。

  • 构造函数
  • 创建消息函数
  • 发送消息函数
  • 移除消息函数
  • 消息分发及处理函数
构造函数

构造函数主要是对成员变量进行初始化,获取线程中的Looper、MessageQueue等对象。

[java] 
view plain
copy
  1. /** 
  2.  * Default constructor associates this handler with the queue for the 
  3.  * current thread. 
  4.  * 
  5.  * If there isn't one, this handler won't be able to receive messages. 
  6.  */  
  7. public Handler() {  
  8.     if (FIND_POTENTIAL_LEAKS) {  
  9.         final Class<? extends Handler> klass = getClass();  
  10.         if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  11.                 (klass.getModifiers() & Modifier.STATIC) == 0) {  
  12.             Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  13.                 klass.getCanonicalName());  
  14.         }  
  15.     }  
  16.   
  17.     mLooper = Looper.myLooper();  
  18.     if (mLooper == null) {  
  19.         throw new RuntimeException(  
  20.             "Can't create handler inside thread that has not called Looper.prepare()");  
  21.     }  
  22.     mQueue = mLooper.mQueue;  
  23.     mCallback = null;  
  24. }  

默认构造函数通过Looper.myLooper()函数从当前线程中获取Looper对象,如果Looper对象不存在,那么Handler构造就失败了,会抛出RuntimeException,而MessageQueue是由Looper对象创建出来的,因此,mQueue直接便能从Looper中获取。对于UI线程,在程序初始化时,实际上looper对象就已被创建出来(通过调用Looper.prepare()进行创建,并把looper对象存放到一个静态的sThreadLocal中),因此,正常情况下,当我们new出来的Handler不指明任何参数时,实际上就是会默认关联到UI线程。但是,但如果该对象是在某个线程的Run方法中被创建出来,那么它会被关联到该后台线程。“关联到该线程”的意思实际上就是,当Handler关联到UI线程,那最终发送的消息是加到了UI线程的消息队列,如果它关联到后台线程,则发送的消息加到了后台线程的消息队列。下面的这种方式,mHandler会被直接关联到指定的线程。

[java] 
view plain
copy
  1. new Thread(new Runnable() {  
  2.     @Override  
  3.     public void run() {  
  4.         Looper.prepare();  
  5.           
  6.         Handler mHandler = new Handler() {  
  7.             public void handleMessage(Message msg) {  
  8.                 // process incoming messages here  
  9.             }  
  10.         };  
  11.   
  12.         Looper.loop();  
  13.     }  
  14. });  
创建消息函数:obtainMessage()及其系列函数
[java] 
view plain
copy
  1. public final Message obtainMessage(int what, int arg1, int arg2, Object obj)  
  2. {  
  3.     return Message.obtain(this, what, arg1, arg2, obj);  
  4. }  

这些函数都很简单,无非是通过Message.obtain(…)方法创建消息。obtain方法已在Message类中进行相关说明。还记得obtain方法的调用过程吗?忘记了的请回过头再看看。

发送消息函数

发送的过程是由post其系列函数和send系列函数进行的。如下:

[java] 
view plain
copy
  1. post(Runnable)  
  2. postAtTime(Runnable, long)  
  3. postAtTime(Runnable, Object, long)  
  4. postDelayed(Runnable, long)  
  5. postAtFrontOfQueue(Runnable)  
  6. sendMessage(Message)  
  7. sendEmptyMessage(int)  
  8. sendEmptyMessageDelayed(intlong)  
  9. sendEmptyMessageAtTime(intlong)  
  10. sendMessageDelayed(Message, long)  
  11. sendMessageAtTime(Message, long)  
  12. sendMessageAtFrontOfQueue(Message)  

先说sendXXXMessage系列函数,这些函数提供了很多可选接口,主要是可使用delayMillis。如sendMessageDelayed(Message msg, long delayMillis),该函数并不是说把消息延迟xxx毫秒后发送,而是延迟将消息分发下来。即消息加入消息队列后,message会隔xxx毫秒后从消息队列中被取出来执行。
这些函数最终都是调用的sendMessageAtTime函数。

[java] 
view plain
copy
  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis)  
  2. {  
  3.     boolean sent = false;  
  4.     MessageQueue queue = mQueue;  
  5.     if (queue != null) {  
  6.         msg.target = this;  
  7.         sent = queue.enqueueMessage(msg, uptimeMillis);  
  8.     }  
  9.     else {  
  10.         RuntimeException e = new RuntimeException(  
  11.             this + " sendMessageAtTime() called with no mQueue");  
  12.         Log.w("Looper", e.getMessage(), e);  
  13.     }  
  14.     return sent;  
  15. }  

该方法就是调用MessageQueue的enqueueMessage(…)方法将指定的Message插入到消息队列中去,即加入Message链,并指明何时应该从消息队列中取出来执行。其中uptimeMillis就是绝对时间戳,uptimeMillis = current time + delayMillis。
postXXX系列函数来说,需要指定一个Runnable对象,在合适的时间执行。
实际上,他们最终调用的还是sendMessageAtTime函数,只不过中间多了一步,即根据Runnable创建Message对象。如:

[java] 
view plain
copy
  1. public final boolean postAtTime(Runnable r, long uptimeMillis)  
  2. {  
  3.    return  sendMessageAtTime(getPostMessage(r), uptimeMillis);  
  4. }  

其中,getPostMessage代码如下:就是通过obtain方法获取一个Message,并设置其callback。

[java] 
view plain
copy
  1. private final Message getPostMessage(Runnable r) {  
  2.     Message m = Message.obtain();  
  3.     m.callback = r;  
  4.     return m;  
  5. }  
移除消息系列函数
[java] 
view plain
copy
  1. removeCallbacks(Runnable)  
  2. removeCallbacks(Runnable, Object)  
  3. removeCallbacksAndMessages(Object)  
  4. removeMessages(int)  
  5. removeMessages(int, Object)  

这些函数的作用就是从当前消息队列中移除掉所有指定ID或指定Runnable对象的Message。如:

[java] 
view plain
copy
  1. public final void removeCallbacks(Runnable r)  
  2. {  
  3.     mQueue.removeMessages(this, r, null);  
  4. }  

上面的所有函数完成了第一个功能,即消息的发送,并加入到消息队列中去。

但发送完后,并不是立即就能执行,当消息从消息泵中被取出来后,才行执行。因此,消息的发送和处理实际上是一个异步的过程。

消息分发及处理函数

当消息从消息泵中抽取出来后,就会进行消息的分发。
消息抽取的过程需要参见Looper的核心处理函数:

[java] 
view plain
copy
  1. public static final void loop() {  
  2.     Looper me = myLooper();  
  3.     MessageQueue queue = me.mQueue;  
  4.     while (true) {  
  5.         Message msg = queue.next(); // might block  
  6.         if (msg != null) {  
  7.             if (msg.target == null) {  
  8.                 // No target is a magic identifier for the quit message.  
  9.                 return;  
  10.             }  
  11.            <strong> </strong>msg.target.dispatchMessage(msg);  
  12.             msg.recycle();  
  13.         }  
  14.     }  
  15. }  

从上面可以看出,loop函数本身就是一个回路(死循环),不停的调用queue.next()函数从消息链中取出消息(如果取不到消息就会被阻塞住)。然后通过MSG的target成员变量(Handler)来调用其dispatchMessage方法将消息分发下去,然后执行消息处理函数。

[java] 
view plain
copy
  1. public void dispatchMessage(Message msg) {  
  2.     if (msg.callback != null) {  
  3.         handleCallback(msg);  
  4.     } else {  
  5.         if (mCallback != null) {  
  6.             if (mCallback.handleMessage(msg)) {  
  7.                 return;  
  8.             }  
  9.         }  
  10.         handleMessage(msg);  
  11.     }  
  12. }  

在消息的分发的过程中,其执行是有优先级的:

如果Message中包含callback(即通过post系列函数设置的Runnable对象),那么它会被优先执行。

否则,如果给当前的Handler设置了mCallback,那么它会优先执行。如果该方法返回true,分发结束,处理完毕。如果返回false,那么他还有机会执行默认的handleMessage函数。

以下是三种情况的示例代码:

[java] 
view plain
copy
  1. 1.   
  2.     Handler mHandler = new Handler();  
  3.     mHandler.post(new Runnable() {  
  4.         @Override  
  5.         public void run() {  
  6.             // TODO something  
  7.         }  
  8.     });  
  9. 2.   
  10.     Handler mHandler = new Handler(new Handler.Callback()  
  11.     {  
  12.           
  13.         @Override  
  14.         public boolean handleMessage(Message msg) {  
  15.            // TODO something  
  16.             return false;  
  17.         }  
  18.     });  
  19. 3.   
  20.     protected Handler m_handler = new Handler()  
  21.     {  
  22.         @Override  
  23.         public void handleMessage(Message msg)  
  24.         {  
  25.             // TODO something  
  26.         }  
  27.     });  

MessageQueue源码分析

成员变量
我们先看一下它的成员变量
[java] 
view plain
copy
  1. Message mMessages;  
  2. private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();  
  3. private IdleHandler[] mPendingIdleHandlers;  
  4. private boolean mQuiting;  
  5. boolean mQuitAllowed = true;  
  6. // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.  
  7. private boolean mBlocked;  

mMessages:在最初不了解Message类时,以为MessageQueue里面,存放的是一个类似于LinkedList<Message>的数据结构。当了解Message数据结构后,才知道MessageQueue里面维护的只有一个Message,因为Message本身就能构建一个Message链。MessageQueue主要功能就是维护这个Message链,如插入和删除Message链中的元素,并提供获取Message链中next  Message的方法。这里,mMessages始终指向的是消息链的第一个节点,即头节点。

mIdleHandlers:外部注册的回调列表(listeners)。如果当前消息队列已没有新的Message能被取出来时,线程即将被阻塞前被调用。即线程处于空闲时间时,被调用。

mPendingIdleHandlers:该成员变量配合上面的mIdleHandlers使用,我觉得它没有必要保存起来,完全用一个临时变量即可。

mQuiting:当前消息队列是否已准备退出。实际上,如果它为True,也就表明当前线程将会立马结束掉。

mQuitAllowed:是否允许退出消息队列。对于主线程(UI线程),该标志量为true。

mBlocked:标志当前消息队列是否处于阻塞状态。

下面的接口定义了线程处于空闲状态时的回调函数。由此可以看出,当你想在线程不忙的时候干点其它事情的话,这个接口就能派得上用场了。

[java] 
view plain
copy
  1. public static interface IdleHandler {  
  2.     boolean queueIdle();  
  3. }  

以下方法用来注册和移除线程处于空闲状态时的回调函数。

[java] 
view plain
copy
  1. public final void addIdleHandler(IdleHandler handler) {  
  2.     if (handler == null) {  
  3.         throw new NullPointerException("Can't add a null IdleHandler");  
  4.     }  
  5.     synchronized (this) {  
  6.         mIdleHandlers.add(handler);  
  7.     }  
  8. }  
  9.   
  10. public final void removeIdleHandler(IdleHandler handler) {  
  11.     synchronized (this) {  
  12.         mIdleHandlers.remove(handler);  
  13.     }  
  14. }  

再来看一看MessageQueue的两个核心函数:enqueueMessage(…)与next()函数。

enqueueMessage(Message msg, long when)函数

该函数的目的就是把指定的Message按照绝对时间插入到当前的消息队列中去。还记得该函数是在哪里被调用的吗?请回过头看看Handler的源代码:sendMessageAtTime(Message msg, long uptimeMillis)

[java] 
view plain
copy
  1. final boolean enqueueMessage(Message msg, long when) {  
  2.     // 1. 如果MSG的绝对时间戳不为0(说明已被初始化,并已加入到队列中),抛出异常  
  3.     if (msg.when != 0) {  
  4.         throw new AndroidRuntimeException(msg  
  5.                 + " This message is already in use.");  
  6.     }  
  7.       
  8.     // 2. 如果MSG的target为null,说明该消息是QUIT消息。如果此时又发现mQuitAllowed为false,则抛出异常。  
  9.     // 实际上,如果你调用主线程的Looper.quit()方法,你会发现该异常会被抛出来。因为主线程的消息循环是不允许退出的。  
  10.     if (msg.target == null && !mQuitAllowed) {  
  11.         throw new RuntimeException("Main thread not allowed to quit");  
  12.     }  
  13.     final boolean needWake;  
  14.     synchronized (this) {  
  15.         // 3. 如果消息队列正在退出,则直接返回false;否则,查看当前要加入的MSG是否是要求退出消息队列的MSG  
  16.         //(QUIT MSG:判断依据是MSG的target是否为空),如果是,则将mQuiting设置为true,然后把该消息加入到消息  
  17.         // 队列的头部,以保证下一次通过next()函数取出的消息就是QUIT消息,能快速的终止掉线程。  
  18.         if (mQuiting) {  
  19.             RuntimeException e = new RuntimeException(  
  20.                 msg.target + " sending message to a Handler on a dead thread");  
  21.             Log.w("MessageQueue", e.getMessage(), e);  
  22.             return false;  
  23.         } else if (msg.target == null) {  
  24.             mQuiting = true;  
  25.         }  
  26.           
  27.         // 4. MSG最终从消息队列中取出来的绝对时间戳。NOTE:MSG的when字段只有在这一个地方被主动设置过。  
  28.         msg.when = when;  
  29.         //Log.d("MessageQueue", "Enqueing: " + msg);  
  30.   
  31.         // NOTE:以下部分就是真正如何将消息插入消息链的过程。  
  32.         Message p = mMessages;  
  33.         // 5. 如果当前的消息链为空,或者要插入的MSG为QUIT消息,或者要插入的MSG时间小于消息链的第一个消息。  
  34.         // 那么,强势插入到消息链的头部。显示,消息链的头部被改变了,变成了新添加的消息。needWake需要根据  
  35.         // mBlocked的情况考虑是否触发。  
  36.         if (p == null || when == 0 || when < p.when) {  
  37.             msg.next = p;  
  38.             mMessages = msg;  
  39.             needWake = mBlocked; // new head, might need to wake up  
  40.         } else {  
  41.             //  6. 否则,我们需要遍历该消息链,将该MSG插入到合适的位置。整个消息链是按消息被取出的绝对时间戳  
  42.             // 由小到大链接起来的。时间轴为:msg1.when <= msg2.when <= msg3.when <= msg4.when <= msg5.when……  
  43.             Message prev = null;  
  44.             while (p != null && p.when <= when) {  
  45.                 prev = p;  
  46.                 p = p.next;  
  47.             }  
  48.             msg.next = prev.next;  
  49.             prev.next = msg;  
  50.             needWake = false// still waiting on head, no need to wake up  
  51.         }  
  52.     }  
  53.   
  54.     // 7. 对于needWake,如果该变量为true,说明mBlocked为true,即当前线程处于阻塞状,也即nativePollOnce处于阻塞状态。但此时,  
  55.     // 我们已通过这个enqueueMessage方法已经向消息链中添加了一个消息,也就是说,此时我们需要把阻塞状态变成非阻塞状态,  
  56.     // 让next()函数能够取到MSG。怎么办?通过执行nativeWake方法,便能触发nativePollOnce函数结束等待。实际上,  
  57.     // nativePollOnce和nativeWake内部是通过管道的机制来实现阻塞和接触阻塞的。我的理解是: nativePollOnce函数从管道中  
  58.     // 读数据,如果发现管道中有数据,则立即返回,否则,一直等待。而nativeWake就是向管道中写数据,只要往管道的另一端写数据,  
  59.     // 则nativePollOnce就能立马从管道中读出数据来,从而变成非阻塞状态。(请参考:http://book.51cto.com/art/201208/353352.htm)<

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值