惊天秘密!从Thread开始,揭露Android线程通讯的诡计和主线程的阴谋

背景介绍

我们在Android开发过程中,几乎都离不开线程。但是你对线程的了解有多少呢?它***运行的背后,究竟隐藏了多少不为人知的秘密呢?线程间互通暗语,传递信息究竟是如何做到的呢?Looper、Handler、MessageQueue究竟在这背后进行了怎样的运作。本期,让我们一起从Thread开始,逐步探寻这个***的线程链背后的秘密。

注意,大部分分析在代码中,所以请仔细关注代码哦!

从Tread的创建流程开始

在这一个环节,我们将一起一步步的分析Thread的创建流程。

话不多说,直接代码里看。

线程创建的起始点init()

 
 
 
 
  1. // 创建Thread的公有构造函数,都调用的都是这个私有的init()方法。我们看看到底干什么了。 
  2.  
  3. /** 
  4.  
  5.      * 
  6.  
  7.      * @param 线程组 
  8.  
  9.      * @param 就是我们平时接触最多的Runnable同学 
  10.  
  11.      * @param 指定线程的名称 
  12.  
  13.      * @param 指定线程堆栈的大小 
  14.  
  15.      */ 
  16.  
  17. private void init(ThreadGroup g, Runnable target, String name, long stackSize) { 
  18.  
  19.         Thread parent = currentThread();            //先获取当前运行中的线程。这一个Native函数,暂时不用理会它怎么做到的。黑盒思想,哈哈! 
  20.  
  21.         if (g == null) { 
  22.  
  23.             g = parent.getThreadGroup();            //如果没有指定ThreadGroup,将获取父线程的TreadGroup 
  24.  
  25.         } 
  26.  
  27.         g.addUnstarted();                           //将ThreadGroup中的就绪线程计数器增加一。注意,此时线程还并没有被真正加入到ThreadGroup中。 
  28.  
  29.         this.group = g;                             //将Thread实例的group赋值。从这里开始线程就拥有ThreadGroup了。 
  30.  
  31.         this.target = target;                       //给Thread实例设置Runnable。以后start()的时候执行的就是它了。 
  32.  
  33.         this.priority = parent.getPriority();       //设置线程的优先权重为父线程的权重 
  34.  
  35.         this.daemon = parent.isDaemon();            //根据父线程是否是守护线程来确定Thread实例是否是守护线程。 
  36.  
  37.         setName(name);                              //设置线程的名称   
  38.  
  39.         init2(parent);                              //纳尼?又一个初始化,参数还是父线程。不急,稍后在看。 
  40.  
  41.         /* Stash the specified stack size in case the VM cares */ 
  42.  
  43.         this.stackSize = stackSize;                 //设置线程的堆栈大小 
  44.  
  45.         tid = nextThreadID();                       //线程的id。这是个静态变量,调用这个方法会自增,然后作为线程的id。 
  46.  
  47.     }  

第二个init2() 

 

 

 

至此,我们的Thread就初始化完成了,Thread的几个重要成员变量都赋值了。

启动线程,开车啦!

通常,我们这样了启动一条线程。

 
 
 
 
  1. Thread threadDemo = new Thread(() -> { 
  2.  
  3.     }); 
  4.  
  5. threadDemo.start();  

那么start()背后究竟隐藏着什么样不可告人的秘密呢?是人性的扭曲?还是道德的沦丧?让我们一起点进start()。探寻start()背后的秘密。

 
 
 
 
  1. //如我们所见,这个方法是加了锁的。原因是避免开发者在其它线程调用同一个Thread实例的这个方法,从而尽量避免抛出异常。 
  2.  
  3. //这个方法之所以能够执行我们传入的Runnable里的run()方法,是应为JVM调用了Thread实例的run()方法。 
  4.  
  5. public synchronized void start() { 
  6.  
  7.         //检查线程状态是否为0,为0表示是一个新状态,即还没被start()过。不为0就抛出异常。 
  8.  
  9.         //就是说,我们一个Thread实例,我们只能调用一次start()方法。 
  10.  
  11.         if (threadStatus != 0) 
  12.  
  13.             throw new IllegalThreadStateException(); 
  14.  
  15.         //从这里开始才真正的线程加入到ThreadGroup组里。再重复一次,前面只是把nUnstartedThreads这个计数器进行了增量,并没有添加线程。 
  16.  
  17.         //同时,当线程启动了之后,nUnstartedThreads计数器会-1。因为就绪状态的线程少了一条啊! 
  18.  
  19.         group.add(this); 
  20.  
  21.         started = false
  22.  
  23.         try { 
  24.  
  25.             nativeCreate(this, stackSize, daemon);  //又是个Native方法。这里交由JVM处理,会调用Thread实例的run()方法。 
  26.  
  27.             started = true
  28.  
  29.         } finally { 
  30.  
  31.             try { 
  32.  
  33.                 if (!started) { 
  34.  
  35.                     group.threadStartFailed(this);  //如果没有被启动成功,Thread将会被移除ThreadGroup,同时,nUnstartedThreads计数器又增量1了。 
  36.  
  37.                 } 
  38.  
  39.             } catch (Throwable ignore) { 
  40.  
  41.                 
  42.  
  43.             } 
  44.  
  45.         } 
  46.  
  47.     }  

好吧,最精华的函数是native的,先当黑盒处理吧。只要知道它能够调用到Thread实例的run()方法就行了。那我们再看看run()方法到底干了什么神奇的事呢? 

 

 

 

黑实验 

 

 

 

上面的实验表明了,我们完全可以用Thread来作为Runnable。

几个常见的线程手段(操作)

Thread.sleep()那不可告人的秘密

我们平时使用Thread.sleep()的频率也比较高,所以我们在一起研究研究Thread.sleep()被调用的时候发生了什么。

在开始之前,先介绍一个概念——纳秒。1纳秒=十亿分之一秒。可见用它计时将会非常的精准。但是由于设备限制,这个值有时候并不是那么准确,但还是比毫秒的控制粒度小很多。

  1. //平时我们调用的Thread.sleep(long)***调用到这个方法来,后一个陌生一点的参数就是纳秒。 
  2.  
  3. //你可以在纳秒级控制线程。 
  4.  
  5. public static void sleep(long millis, int nanos) 
  6.  
  7.     throws InterruptedException { 
  8.  
  9.         //下面三个检测毫秒和纳秒的设置是否合法。 
  10.  
  11.         if (millis < 0) { 
  12.  
  13.             throw new IllegalArgumentException("millis < 0: " + millis); 
  14.  
  15.         } 
  16.  
  17.         if (nanos < 0) { 
  18.  
  19.             throw new IllegalArgumentException("nanos < 0: " + nanos); 
  20.  
  21.         } 
  22.  
  23.         if (nanos > 999999) { 
  24.  
  25.             throw new IllegalArgumentException("nanos > 999999: " + nanos); 
  26.  
  27.         } 
  28.  
  29.      
  30.  
  31.         if (millis == 0 
THE END