网上总是说Runable和Thread可以实现线程,这导致我对Thread和Runable有错误的理解,谁让当时不求甚解,让我一直以为实现Runable可以开启线程。

看过源码后进行区分这两者。

无论怎么样,线程都是通过Thread创建的。

其一:Runable只是一个接口,不会开启一个线程,依旧是运行在UI线程中。                     

public interface Runnable {

    /**
* Starts executing the active part of the class' code. This method is
* called when a thread is started that has been created with a class which
* implements {@code Runnable}.
*/
public void run();
}

  可以看到,Runable在源码中只有run方法,并且Runable可以在主线程执行修改UI的代码,并且“OK”的执行一定是在输出10个“runable”后,所以,Runable并没有开启线程,依旧是运行在UI线程中

protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
runnable.run();
Log.d("hello", "OK"); }
Runnable runnable = new Runnable() {
@Override
public void run() {
for(int i = 0;i<10;i++) {
textView.setText("runable");
Log.d("hello", "runable");
}
}
};

其二:Thread如何开启的线程,和Runable什么关系?

源码部分:

public class Thread implements Runnable {

最直接的一部分,实现Runable的接口。下面看看Thread的构造函数。

public Thread() {
create(null, null, null, 0);
}

这是其中之一的构造函数,调用了create()方法,其他构造函数依然调用该方法。下面看看create()方法。

private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
Thread currentThread = Thread.currentThread();
if (group == null) {
group = currentThread.getThreadGroup();
} if (group.isDestroyed()) {
throw new IllegalThreadStateException("Group already destroyed");
} this.group = group; synchronized (Thread.class) {
id = ++Thread.count;
} if (threadName == null) {
this.name = "Thread-" + id;
} else {
this.name = threadName;
} this.target = runnable;

     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);
}

 虽然比较多,但是只需要看这一行代码,this.taeget = runable;而在Thread类的run()方法和start()方法中,

  public void run() {
if (target != null) {
target.run();
}
}

  

/**
* Starts the new Thread of execution. The <code>run()</code> method of
* the receiver will be called by the receiver Thread itself (and not the
* Thread calling <code>start()</code>).
*
* @throws IllegalThreadStateException - if this thread has already started.
* @see Thread#run
*/
public synchronized void start() {
checkNotStarted(); hasBeenStarted = true; nativeCreate(this, stackSize, daemon);
}

这两个形成了两个方法的区别:

  •   start: 

           用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。

  •   run:
      run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void.。

这样也就很清晰可见,那么如何开启线程,

    其中之一,和上文类比。

 Thread t = new Thread(runnable);
t.start();
Log.d("hello","ok");
}
Runnable runnable = new Runnable() {
@Override
public void run() {
           textview.settext(“runable”);
for(int i = 0;i<10;i++) {
Log.d("hello", "runable");
}
}
};
  1. 执行Thread的构造方法,然后执行oncreate方法。this.target = runable; 
  2. 执行t.start();等得到cpu时间片,执行run()方法。
  3. 执行run()方法,target.run();  (也就是在子线程执行runable.run()。)

根据执行结果也看到真正的开启了线程,“OK”的输出并不按代码执行顺序。

06-13 19:32:26.252 21369-21369/? D/hello: ok
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable

但是可以修改Ui(textview.settext)?!!!!!!执行的是runable.run()。(不应该在子线程执行这句话吗?子线程不是不能修改UI吗?)

  

为其添加按钮监听,打印线程 t 的名字,为Thread-76777  

  button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
t.start();
}
});

 得到我想要的结果:: FATAL EXCEPTION: Thread-76777 ,android.view.ViewRootImpl$CalledFromWrongThreadException:

    那么,问题在于。为什么上述情况可以修改,而添加按钮点击后执行t.start()无法修改UI。

  希望有人看到帮我解答一下这个疑问,如果本文有错误也希望指出。转载请注明出处http://www.cnblogs.com/yuhanghzsd/p/5581918.html

原因:

onCreate中极短的时间内, viewRootImpl还没有创建完成, new Thread()是可以“修改”ui的.

因为那个时候viewRootImpl还没有创建.

当使用按钮之后, 就不行了. 因为这个时候的view树肯定是已经创建完毕了. 
只有创建该View的线程才能修改和更新改view.
因为ViewRootImpl是在ActivityThread类中的主线程创建的. 所以只有主线程能更新ui.

 END!

android Thread和Runable区别,精讲(有疑问)的更多相关文章

  1. -L -Wl,-rpath-link -Wl,-rpath区别精讲

    目录 前言 源码准备 源码内容 尝试编译,保证源码没有问题 编译 首先编译world.c 编译并链接hello.c 调试编译test.c 结论 转载请注明出处,谢谢 https://www.cnblo ...

  2. Thread和Runable的区别、Synchronized锁关键字

    一.Thread和Runable的区别 Thread是基类,子类必继承他实现其run方法.其也是实现了Runable接口.Thread是普通的类,并非抽象类或者密封类等. Runnable是接口,子类 ...

  3. Java岗 面试考点精讲(基础篇01期)

    即将到来金三银四人才招聘的高峰期,渴望跳槽的朋友肯定跟我一样四处找以往的面试题,但又感觉找的又不完整,在这里我将把我所见到的题目做一总结,并尽力将答案术语化.标准化.预祝大家面试顺利. 术语会让你的面 ...

  4. Gradle系列之一 Groovy语法精讲

    Gradle技术之一 Groovy语法精讲 gradle脚本是基于groovy语言开发的,想要学好gradle必须先要对groovy有一个基本的认识 1. Groovy特点 groovy是一种DSL语 ...

  5. Java并发编程核心知识体系精讲

    第1章 开宗明义[不看错过一个亿]本章一连串设问:为什么学并发编程?学并发编程痛点?谁适合学习本课?本课程包含内容和亮点?首先4大个理由告诉你为什么要学,其实源于JD岗位要求就不得不服了.其次5个痛点 ...

  6. 多线程爬坑之路-Thread和Runable源码解析

    多线程:(百度百科借一波定义) 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提 ...

  7. 深入Java核心 Java内存分配原理精讲

    深入Java核心 Java内存分配原理精讲 栈.堆.常量池虽同属Java内存分配时操作的区域,但其适用范围和功用却大不相同.本文将深入Java核心,详细讲解Java内存分配方面的知识. Java内存分 ...

  8. Thread与Runable

    当我们继承Thread的时候可以看出. 调用run()与start()来启动多线程是有区别. 如下我们开启多线程时 MyThread  mt1 = new MyThread(“线程A”); MyThr ...

  9. 【C++自我精讲】基础系列一 指针与引用

    [C++自我精讲]基础系列一 指针与引用   一 前言   指针.引用.指针与引用区别. 二 指针   变量:代码中常常通过定义变量来申请并命名存储空间,并通过变量的名字来使用这段存储空间. //变量 ...

随机推荐

  1. RegEx正则表达式学习笔记

    一.实用的例子 public static void main(String[] args) { // 简单练习 System.out.println("-123".matches ...

  2. Linux/Unix shell 脚本监控磁盘可用空间

    Linux下监控磁盘的空闲空间的shell脚本,对于系统管理员或DBA来说,必不可少.下面是给出的一个监控磁盘空间空间shell脚本的样本,供大家参考. 1.监控磁盘的空闲空间shell脚本 robi ...

  3. perl环境配置以及Eclipse安装perl开发插件

    简介: 这篇文章将详细介绍 EPIC 组件的安装,EPIC 编辑环境,调试运行环境,着重介绍如何使用 EPIC 来快速.简便.准确地调试 Perl 语言程序,包括对于 Perl 程序的单步执行,断点用 ...

  4. Shell教程4-Shell替换

    如果表达式中包含特殊字符,Shell 将会进行替换.例如,在双引号中使用变量就是一种替换,转义字符也是一种替换. 举个例子: 复制纯文本新窗口   #!/bin/bash a=10 echo -e & ...

  5. CMake实践(3)

    一,本期目标 [sun@localhost t3]$ cat README t3:静态库(.a)与动态库(.so)构建    任务:1,建立一个静态库和动态库,提供HelloFunc函数供其他程序编程 ...

  6. hdu 4539(状态压缩dp)

    题意:曼哈顿距离是指:|x1-x2|+|y1-y2|,只要知道这个概念题意就懂了. 分析:这道题与前面做的几道题有所不同,因为当前行不仅与前一行有关,而且与前两行有关,所以我们开数组的时候还要记录前两 ...

  7. JavaScript对象(窗口对象 定时器对象 )

    1:窗口时间 confirm(str):确认对话框,确认返回trun,取消返回false,但是必须要有两个return:不然就算按下取消也会提交 第一个return:用于保证确认按钮运行 <sc ...

  8. C语言实现strlen

    strlen: #ifndef STRLEN_H #define STRLEN_H #include <stdio.h> // 参考微软的写法 int cat_strlen(const c ...

  9. font-size的探究

    整理网上的资料 font字体,用px,em,100%,rem?分什么情况考虑? 我们逐渐意识到,我们用px作为文字大小的单位,已经出现很多问题.最主要是体现在用户不能灵活的控制文字的大小. 对于大多数 ...

  10. linux c下mysql编程样例

    /* ============================================================================ Name : mysql1.c Auth ...