1.首先纠正一个观点,就是runnable运行在子线程中是错误的观念。runnable只是创建了一个执行任务的对象,但是它本身并不会创建一个新的子线程,Runable只是给你接口让你实现工作线程的工作事务,然后附加到你new thread的线程上或post的线程中,其本身并不会创建线程。

几个常识性的概念需要清楚的:

(1)Runnable中的run方法:run()方法在每个线程启动时都会首先执行,启动几个线程就有几个线程去执行这个run()方法。 run()方法是Runnabl接口的抽象方法。实现Runnabl接口就必须实现它的方法,而这个方法就是线程的入口。

(2)实现了Runnable的类只是定义了一个任务,一般还要把实现了Runnable的类的对象传递给一个Thread对象才能开启线程。开启线程的两种方式:

一:

  1. Runnable r=new Runnable(){
  2. public void run() {
  3. while(true){
  4. System.out.println("1111");
  5. }
  6. }
  7. }
  8. Thread t = new Thread(r);
  9. t.start();

或者下面的形式也可以开启一个线程,也就是重写Thread类的run方法:

二:

  1. class First extends Thread{
  2. public void run(){
  3. while(true){
  4. System.out.println("11111111");
  5. }
  6. }
  7. }
  8. public class Test{
  9. public static void main(String []args){
  10. Thread t=new First();
  11. t.start();
  12. }
  13. }

(3)跟进源码一探究竟:

你可以再看一下Thread类的源代码:

  1. private Runnable target;
  2.  
  3. public Thread(Runnable target) {
  4. init(null, target, "Thread-" + nextThreadNum(), 0);
  5. }
  6.  
  7. private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
  8. //其它代码
  9. this.target = target;
  10. //其它代码
  11. }
  12.  
  13. public void run() {
  14. if (target != null) {
  15. target.run();
  16. }
  17. }

所以使用第一种方式开启线程的时候,target就是构造方法传过来的Runnable对象,就会去执行target的run方法,也就是Runnable对象的任务,如果是第二种方式,则执行已经重写过的run方法里的任务。

再看一下Thread类的start方法:

  1. public synchronized void start() {
  2. if (threadStatus != 0)
  3. throw new IllegalThreadStateException();
  4. group.add(this);
  5. start0();
  6. if (stopBeforeStart) {
  7. stop0(throwableFromStop);
  8. }
  9. }
  10.  
  11. private native void start0();

start0方法用的native关键字,百度了一下,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。

可以再看一下JDK的文档,关于Thread类的start方法的介绍:
public void start()
         使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

所以我觉得start方法里应该就是做了让虚拟机调用该线程的 run 方法,也就是Thread类的run方法,也就执行了run方法里的任务

Thread的run方法是调用Runnable的run方法的。Thread类是implements了Runnable接口;调用的start()方法,其实就是Thread类中的start()方法;代理设计模式。

2.handler.post在主线程中执行,具体代码:

自定义的线程中是不能更新UI的,但是如果遇到更新UI的事情,我们可以用handler的post()方法来将更新UI的方法体,直接传送到主线程中,这样就能直接更新UI了。Handler的post()方法就是将Runnable中的代码段传送到主线程。

  1. public class MainActivity extends Activity {
  2. TextView valueTv;
  3. public Handler mHandler;
  4. private MyThread thread;
  5. // 定义一个自己的线程
  6. class MyThread extends Thread {
  7. @Override
  8. public void run() {
  9. System.out.println("线程开始运行");
  10. Runnable r = new Runnable() {
  11. @Override
  12. public void run() {
  13. valueTv.setTextColor(Color.RED);
  14. valueTv.setTextSize(30);
  15. valueTv.setText("从线程中传过来的代码段");
  16. System.out.println("执行runnable代码的线程:"+Thread.currentThread().getName());
  17. }
  18. };
  19. //上面代码中的runnable线程体经过post后会直接传送到主线程中执行修改字体的操作。
  20. //post直接可以把一段代码当做变量一样传递,但是请不要传送耗时操作的代码到主线程中
  21. mHandler.post(r);
  22. }
  23. }
  24.  
  25. @Override
  26. protected void onCreate(Bundle savedInstanceState) {
  27. super.onCreate(savedInstanceState);
  28. setContentView(R.layout.activity_main);
  29. valueTv = (TextView)findViewById(R.id.vale_textView);
  30. mHandler = new Handler();
  31. thread = new MyThread();
  32. // 启动线程
  33. thread.start();
  34. }
  35. }

方法的官方解释是:

The runnable will be run on the thread to which this handler is attached.

既是说,这个开启的runnable会在这个handler所依附线程中运行,而这个handler是在UI线程中创建的,所以 
自然地依附在主线程中了。

postDelayed(new Runnable()) 而没有重新生成新的 New Thread()

3.View.post(Runnalbe)方法,在post(Runanble action)方法中,View获得当前主线程(即UI线程)的handler,然后将action对象post到handler里面去,在Handler里,它将传递过来的action对象封装成一个Message(Message 的callback为action),然后将其投入到UI线程的消息循环中,在handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法,而此时,已经路由到UI线程里,因此我们可以毫无顾虑来更新UI。

4.主要参考如下博客:

http://blog.sina.com.cn/s/blog_77c6324101016jp8.html

https://www.cnblogs.com/huaqing-wkc/p/4940190.html

常见的新建线程的方法是:

Thread thread = new Thread();

thread.start();

HandlerThread thread = new HandlerThread("string");

thread.start();

2017-11-29 由runnable说起Android中的子线程和主线程的更多相关文章

  1. Android 线程与主线程

    网络连接需要时间.Web服务器可能需要1~2秒的时间来响应,文件下载则耗时更久.考虑到这个因素,Android禁止任何主线程网络连接行为.即使强行为之,Android也会抛出NetworkOnMain ...

  2. Android中,子线程使用主线程中的组件出现问题的解决方法

    Android中,主线程中的组件,不能被子线程调用,否则就会出现异常. 这里所使用的方法就是利用Handler类中的Callback(),接受线程中的Message类发来的消息,然后把所要在线程中执行 ...

  3. 2017.11.29 JSP+Servlet 中功能验证码及验证的实现

    源代码如下: validate.jsp <%@ page language="java" import="java.util.*" pageEncodin ...

  4. Android关于主线程和非主线程

    必须在主线程执行的任务: (1)UI更新 必须在非主线程中执行的任务 (1)Http请求 如执行:ImageHelper.getInstance().loadImageSync(picUrl); 外面 ...

  5. (5.11)mysql高可用系列——复制中常见的SQL与IO线程故障

    关键词:mysql复制故障处理 [1]手工处理的gtid_next(SQL线程报错) 例如:主键冲突,表.数据库不存在,row模式下的数据不存在等. [1.1]模拟故障:GTID模式下的重复创建用户 ...

  6. 第11讲- Android中进程及其优先级

    第11讲Android中进程及其优先级 进程与线程: 进程:操作系统结构的基础,资源分配的最小单元,一个操作系统包括多个进程: 线程:线程存在于进程当中,是操作系统调试执行的最小单元,一个进程包括多个 ...

  7. Android中进程与线程

    常说的主线程(UI线程)是什么? 当一个Android程序刚启动的时候,我们的android系统就会启动一个带有一个单一线程的linux进程.默认情况下,所有的组件比如Activity都运行在同样的一 ...

  8. Android中的Handler的具体用法

    Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行.Android利用Handler来实现UI线程的更新的. Handler是Android中的消息发送器,其在哪个Activit ...

  9. Android(java)学习笔记208:Android中操作JSON数据(Json和Jsonarray)

    1.Json 和 Xml       JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于ECMAScript的一个子集. JSON采用完全独立于语言的 ...

随机推荐

  1. JFinal中文件上传后会默认放置到WebContent的upload包下,但是tomcat会自动重启,当我们再次打开upload文件夹查看我们刚刚上传的文件时,发现上传的文件已经没有了。

    JFinal中文件上传后会默认放置到WebContent的upload包下,但是tomcat会自动重启,当我们再次打开upload文件夹查看我们刚刚上传的文件时,发现上传的文件已经没有了.因为tomc ...

  2. PAT 1081 检查密码(15) (代码+思路)

    1081 检查密码(15 分) 本题要求你帮助某网站的用户注册模块写一个密码合法性检查的小功能.该网站要求用户设置的密码必须由不少于6个字符组成,并且只能有英文字母.数字和小数点 .,还必须既有字母也 ...

  3. BZOJ1855或洛谷2569 [SCOI2010]股票交易

    一道单调队列优化\(DP\) BZOJ原题链接 洛谷原题链接 朴素的\(DP\)方程并不难想. 定义\(f[i][j]\)表示到第\(i\)天,手上持有\(j\)股时的最大收益. 转移方程可以分成四个 ...

  4. ubuntu下安装配置ADB

    1.下载SDK Tools for Linux,地址:http://developer.android.com/sdk/index.html 2.解压,将 android-sdk-linux 文件夹放 ...

  5. 网页启用Gzip压缩 提高浏览速度

    启用Gzip压缩的好处 它的好处显而易见,提高网页浏览速度,无论是之前说的精简代码.压缩图片都不如启用Gzip来的实在.下图为启用Gzip后的效果. Gzip压缩效率非常高,通常可以达到70%的压缩率 ...

  6. Java Http协议处理类

    public class HttpRequest { public static String doGet(String url,String params) throws Exception{ re ...

  7. c++文件中引用C代码

    下面提供一个比较完整的示例程序,一共有四个文件:main.cpp.test.c.test.h.test.hpp main.cpp #include "test.hpp" int m ...

  8. RAC初步使用

    信号基本流程: //1:创建信号 RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSub ...

  9. jQuery动画函数回调

    $("#show").click(function () { //function 是显示完成之后的回调函数 $("p").show(2000,function ...

  10. partition

    一.背景 1.在Hive Select查询中一般会扫描整个表内容,会消耗很多时间做没必要的工作.有时候只需要扫描表中关心的一部分数据,因此建表时引入了partition概念. 2.分区表指的是在创建表 ...