Thread的创建方式以及稍微深入的分析:
2015-08-15 11:47
567 查看
通常我们创建一个Thread的方法有两种,一种是直接new一个Thread然后start:
或者是匿名类对象:
这种方法的劣势在于继承方式的耦合性比较高,并且当我们采用第一种方法创建Thread的时候如果我们还要去extends另一个类,由于java不支持多继承,因此这样就不太方便我们的扩展应用了,因此我们一般采用第二种方法
我们先创建出来一个实现了Runnable接口的类,然后是创建对象之后放入Thread中即可
但是通常在android中我们觉得这样写比较麻烦,还需要多创建出来一个类,因此我们又想到了直接使用匿名类即可:
这样我们发现写起来非常的的容易,看起来代码也简洁多了,同样我们需要注意的是start()这个函数必须调用。既然我们看到了创建线程的操作,下面我们来看一下Thread源码是怎么为我们创建出来一个线程,又是怎么通过start()函数调用我们的run()去执行里面的内容的:
首先我们是new Thread()因此进入源码我们可以看到:
调用的是create函数,然后我们看一下create函数的写法:
先介绍一下几个变量的意思:
group:每一个线程都属于一个group,当线程被创建时就会加入一个特定的group,当线程运行结束,会从这个 group 中移除;
runnable:这个就是一个Runnable接口对象的传入,我们可以在外面覆盖其run方法
threadName:就是我们线程的名字, 如果没有显示给线程设置名字,那么就会使用 Thread+id 当作线程的名字,例如HandlerThread中我们的构造函数就是要传入一个name
stackSize:线程栈大小,默认为 0,即使用默认的线程栈大小(由 dalvik 中的全局变量 gDvm.stackSize 决定)
上面这个函数中this就是我们new出来的线程对象,在它被创建之前,我们先要设置一下它的属性,也就是通过传入的参数以及系统默认值设置,如何获取当前的这个线程对象的呢?这就是通过
这句获取的,这里又调用了
这里的vmThread:可视为对 dalvik thread 的简单封装,Thread 类通过 VMThread 里面的 JNI 方法来调用 dalvik 中操作线程的方法,通过它的成员变量 thread 和 vmata,我们可以将 Android Thread 和 dalvik Thread 的关联起来;
现在我们发现了我们只是把this添加到了group之中,但是貌似我们就没看到创建线程这个动作的执行,这是为什么?这也就是我们每次都要强调的为什么需要start()的作用了,其实线程的创建并不是在你new Thread的时候创建的,而是在你start()函数调用的时候创建的
转调 VMThread 的 native 方法 create,再下面的分析就太深了,能力有限,具体可以参考一下大神罗朝辉的分析:http://blog.csdn.net/kesalin/article/details/37659547,很有深度,望尘莫及!
我们只需要知道,在VMThread.create(this, stackSize);中最后回调的是我们的run方法即可
这里我们可能想,当时我们什么参数都没传的时候create函数中的
target肯定为null啊,如果按照函数来看,那应该是走else,也就是什么事情都不做啊,那怎样才能调用run函数呢?
其实我们Thread自己覆盖了run方法,因此根本就不会进入这个函数,而是会调用我们自己run函数中的方法,是不是恍然大悟了!
这里我们把第一种方法创建线程,线程怎样执行的简单的说完了,那么第二种方法的调用流程是什么样的呢,其实是差不多的,先创建thread
第二个参数不是null了,而且我们也没有覆盖我们的Thread中的run方法,因此我们走的路线就是调用target.run()方法,因此runnable中会去执行,
现在我们思考一个问题,那就是像上面的分析,如果我Thread覆盖了run方法,而且又有一个runnable参数,那怎么走?例如:
其实分析第一种写法的时候已经说的很清楚了,如果覆盖了run那么target.run是不会执行的,因此只会去打印System.out.println(“hello Thread run()”);
那么又如果我们想两个run都执行到该怎么做?那就只能是函数调用了
这样写貌似没什么卵用。我们看到了使用的是一个线程因为我们只是VMThread.create(this, stackSize);
了一次,而不是两次,好了,现在我们两种方法的分析都已经分析完了,是不是对线程又有了一个新的认识,分析的不好还请见谅,毕竟水平有限,有问题还请大神们指出来,我好改进!
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);
了一次,而不是两次,好了,现在我们两种方法的分析都已经分析完了,是不是对线程又有了一个新的认识,分析的不好还请见谅,毕竟水平有限,有问题还请大神们指出来,我好改进!
相关文章推荐
- C#线程间不能调用剪切板的解决方法
- C#线程同步的三类情景分析
- C#子线程更新UI控件的方法实例总结
- C++使用CriticalSection实现线程同步实例
- 基于C++实现的线程休眠代码
- VB读取线程、句柄及写入内存的API代码实例
- C#网络编程基础之进程和线程详解
- C#多线程处理多个队列数据的方法
- C#实现线程安全的简易日志记录方法
- C#中线程同步对象的方法分析
- ASP.NET线程相关配置
- 浅析linux环境下一个进程最多能有多少个线程
- 再谈JavaScript线程
- C#实现终止正在执行的线程
- 解析Java线程同步锁的选择方法
- 深入Android线程的相关问题解惑
- 深入探讨linux下进程的最大线程数、进程最大数、进程打开的文件数
- Java线程关闭的3种方法
- JAVA实现线程的三种方法
- 深入Java线程中断的本质与编程原则的概述