Thread的创建方式以及稍微深入的分析:
来源:互联网 发布:finale mac 汉化 编辑:程序博客网 时间:2024/06/01 09:08
通常我们创建一个Thread的方法有两种,一种是直接new一个Thread然后start:
public class TestThread extends Thread { @Override public void run() { System.out.println("做你想做的事情...."); } }/**执行这个线程,我们必须调用它的start()方法,否则线程中的run方法不会执行,下面我们还将详细的说明*/ TestThread test=new TestThread(); test.start();
或者是匿名类对象:
Thread temp = new Thread(){ @Override public void run() { System.out.println("做你想做的事情...."); } }; temp.start();
这种方法的劣势在于继承方式的耦合性比较高,并且当我们采用第一种方法创建Thread的时候如果我们还要去extends另一个类,由于java不支持多继承,因此这样就不太方便我们的扩展应用了,因此我们一般采用第二种方法
public class TestRunnable implements Runnable { @Override public void run() { System.out.println("做你想做的事情...."); } }
我们先创建出来一个实现了Runnable接口的类,然后是创建对象之后放入Thread中即可
TestRunnable r = new TestRunnable(); new Thread(r).start();
但是通常在android中我们觉得这样写比较麻烦,还需要多创建出来一个类,因此我们又想到了直接使用匿名类即可:
new Thread(new Runnable() { @Override public void run() { System.out.println("做你想做的事情...."); } }).start();
这样我们发现写起来非常的的容易,看起来代码也简洁多了,同样我们需要注意的是start()这个函数必须调用。既然我们看到了创建线程的操作,下面我们来看一下Thread源码是怎么为我们创建出来一个线程,又是怎么通过start()函数调用我们的run()去执行里面的内容的:
首先我们是new Thread()因此进入源码我们可以看到:
public Thread() { create(null, null, null, 0);}
调用的是create函数,然后我们看一下create函数的写法:
先介绍一下几个变量的意思:
group:每一个线程都属于一个group,当线程被创建时就会加入一个特定的group,当线程运行结束,会从这个 group 中移除;
runnable:这个就是一个Runnable接口对象的传入,我们可以在外面覆盖其run方法
threadName:就是我们线程的名字, 如果没有显示给线程设置名字,那么就会使用 Thread+id 当作线程的名字,例如HandlerThread中我们的构造函数就是要传入一个name
stackSize:线程栈大小,默认为 0,即使用默认的线程栈大小(由 dalvik 中的全局变量 gDvm.stackSize 决定)
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就是我们new出来的线程对象,在它被创建之前,我们先要设置一下它的属性,也就是通过传入的参数以及系统默认值设置,如何获取当前的这个线程对象的呢?这就是通过
Thread currentThread = Thread.currentThread();
这句获取的,这里又调用了
public static Thread currentThread() { return VMThread.currentThread();}
这里的vmThread:可视为对 dalvik thread 的简单封装,Thread 类通过 VMThread 里面的 JNI 方法来调用 dalvik 中操作线程的方法,通过它的成员变量 thread 和 vmata,我们可以将 Android Thread 和 dalvik Thread 的关联起来;
现在我们发现了我们只是把this添加到了group之中,但是貌似我们就没看到创建线程这个动作的执行,这是为什么?这也就是我们每次都要强调的为什么需要start()的作用了,其实线程的创建并不是在你new Thread的时候创建的,而是在你start()函数调用的时候创建的
public synchronized void start() { checkNotStarted(); hasBeenStarted = true; VMThread.create(this, stackSize); }
转调 VMThread 的 native 方法 create,再下面的分析就太深了,能力有限,具体可以参考一下大神罗朝辉的分析:http://blog.csdn.net/kesalin/article/details/37659547,很有深度,望尘莫及!
我们只需要知道,在VMThread.create(this, stackSize);中最后回调的是我们的run方法即可
public void run() { if (target != null) { target.run(); }}
这里我们可能想,当时我们什么参数都没传的时候create函数中的
this.target = runnable;
target肯定为null啊,如果按照函数来看,那应该是走else,也就是什么事情都不做啊,那怎样才能调用run函数呢?
其实我们Thread自己覆盖了run方法,因此根本就不会进入这个函数,而是会调用我们自己run函数中的方法,是不是恍然大悟了!
这里我们把第一种方法创建线程,线程怎样执行的简单的说完了,那么第二种方法的调用流程是什么样的呢,其实是差不多的,先创建thread
public Thread(Runnable runnable) { create(null, runnable, null, 0); }
第二个参数不是null了,而且我们也没有覆盖我们的Thread中的run方法,因此我们走的路线就是调用target.run()方法,因此runnable中会去执行,
现在我们思考一个问题,那就是像上面的分析,如果我Thread覆盖了run方法,而且又有一个runnable参数,那怎么走?例如:
new Thread(new Runnable() { @Override public void run() { System.out.println("hello TestRunnable run()"); } }){@Override public void run() { System.out.println("hello Thread run()"); }}.start();
其实分析第一种写法的时候已经说的很清楚了,如果覆盖了run那么target.run是不会执行的,因此只会去打印System.out.println(“hello Thread run()”);
那么又如果我们想两个run都执行到该怎么做?那就只能是函数调用了
final Runnable r = new Runnable() { @Override public void run() { System.out.println("hello TestRunnable run()"); System.out.println("Runnable threadid is "+Thread.currentThread().getId()); } }; new Thread(){ @Override public void run() { System.out.println("hello Thread run()"); System.out.println("Thread threadid is "+Thread.currentThread().getId()); r.run(); }}.start();
这样写貌似没什么卵用。我们看到了使用的是一个线程因为我们只是VMThread.create(this, stackSize);
了一次,而不是两次,好了,现在我们两种方法的分析都已经分析完了,是不是对线程又有了一个新的认识,分析的不好还请见谅,毕竟水平有限,有问题还请大神们指出来,我好改进!
- Thread的创建方式以及稍微深入的分析:
- 线程Thread的两种创建方式以及区别
- boost::thread的创建方式
- 线程THread的俩种创建方式
- 14.JavaScript深入之创建对象的多种方式以及优缺点
- 深入理解JAVA创建线程的方法Thread和Runnable
- Thread的两种创建方式的区别
- Thread线程的两种创建方式的传递过程
- Runnable和Thread两种方式创建线程的比较
- 创建线程的方式一:继承Thread类
- Java创建多线程的三种方式---Thread
- 创建线程(Background Thread)的N种方式
- 创建线程的第一种方式:继承Thread…
- 线程的第一种创建方式继承Thread
- 线程Thread 创建方式 容易混淆的概念
- multithreading--创建Thread线程的第一种方式
- multithreading--创建Thread线程的第二种方式,Runnable
- 三种创建多线程的方式(Thread,Runnalbe,callable)
- Error LNK2001 无法解析的外部符号
- C++中delete和delete[]的区别
- Spring各组件之间的功能及其之间的交互
- 开源分布式系统
- 我的ROS入门(五):总算搞通ROS的服务节点订阅发布消息话题了
- Thread的创建方式以及稍微深入的分析:
- 如何new与delete各种二维数组
- 程序封装_随记(1)
- struts2学习笔记(九)struts2内置校验
- hdu 5385 The path(最短路+构造)
- 当修改一些代码时,使用什么编译命令可以最有效率
- 程序封装_随记(2)
- win10机器
- 程序员什么时候该考虑辞职