您的位置:首页 > 其它

Thread的创建方式以及稍微深入的分析:

2015-08-15 11:47 567 查看
通常我们创建一个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);

了一次,而不是两次,好了,现在我们两种方法的分析都已经分析完了,是不是对线程又有了一个新的认识,分析的不好还请见谅,毕竟水平有限,有问题还请大神们指出来,我好改进!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线程