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);
了一次,而不是两次,好了,现在我们两种方法的分析都已经分析完了,是不是对线程又有了一个新的认识,分析的不好还请见谅,毕竟水平有限,有问题还请大神们指出来,我好改进!

0 0
原创粉丝点击