JavaSE第九十九讲:Thread类源码深度剖析

来源:互联网 发布:淘宝宠物医生 编辑:程序博客网 时间:2024/05/01 22:22

  在上一讲内容中,我们已经学习了实现线程的两种方式,但是到底这两种方式有什么关系?为什么实现线程的两种方式中继承Thread类的那个类必须要重写run()方法?为什么用start()方法来启动线程?基于这些问题我们必须去研究线程Thread类的源代码,这样才能只其然且知其所以然,这一讲我们就来讲解Thread类源代码。

1.查看Thread类的源代码可以发现它也实现了Runnable接口
publicclass Thread implements Runnable

1) 因为Runnable接口有一个run()方法,所以Thread类实现了run()方法

查看Thread类的关键代码:Thread类的构造方法:public Thread()和 public Thread(Runnable target)

    public Thread() {init(null, null, "Thread-" + nextThreadNum(), 0);    }
    public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0);    }

比较这两个构造方法可以发现,不带参数的构造方法其实是接受一个Runnable类型的 target 参数为空。

继续跟踪nextThreadNum()方法

    private static int threadInitNumber;    private static synchronized int nextThreadNum() {return threadInitNumber++;    }
返回threadInitNumber++,threadInitNumber是一个什么东西呢?

写一个段代码,在上一讲中程序ThreadTest2的main方法中打印出两个线程的名字,用getName()方法

        Thread t1 = new Thread(new MyThread());        System.out.println(t1.getName());             t1.start();                Thread t2 = new Thread(new MyThread2());        System.out.println(t2.getName());        t2.start();
输出:Thread-0
     Thread-1

继续Thread类的跟踪getName()方法

    public final String getName() {return String.valueOf(name);    }
    private charname[];

name是一个char类型的数组

 private void init(ThreadGroup g, Runnable target, String name,                      long stackSize)this.name = name.toCharArray();

name是init()方法中被赋值的
所以,在Thread()的构造方法中 "Thread-" + nextThreadNum() 这个参数会赋给name,由于nextThreadNum()是一个静态的成员变量,所以会被所有的线程所共享,所以会输出Thrad-0,从0开始标记一个线程的名称。

2. 查看Thread类的其他构造方法:

1). public Thread(String name)

      Allocates a new Thread object. This constructor has the same effect as Thread(null, null, name). 

      [分配一个新的线程对象,name是新线程的名字]

写一个这种构造方法的Demo

package com.ahuier.thread;public class ThreadTest {    public static void main(String[] args) {        Thread1 t1 = new Thread1("first Thread");        Thread2 t2 = new Thread2("second Thread");        System.out.println(t1.getName());        System.out.println(t2.getName());        t1.start();         t2.start();            }}class Thread1 extends Thread{    public Thread1(String name){        super(name);    }    @Override    public void run() {        for(int i = 0; i < 100; i++){            System.out.println("hello world:" + i);        }    }}class Thread2 extends Thread{        public Thread2(String name){        super(name);    }    @Override    public void run() {        for(int i = 0; i < 100; i++){            System.out.println("welcome" + i);        }    }}
编译执行结果不全贴出来了:

first Thread
second Thread
welcome0
welcome1
...

3.  为什么要用start()方法来启动线程,查看他们的源代码

查看Thread类的start()方法:

    public synchronized void start() {        /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added  * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW".         */        if (threadStatus != 0)            throw new IllegalThreadStateException();        group.add(this);        start0();        if (stopBeforeStart) {    stop0(throwableFromStop);}    }    private native void start0();
直接查看start0()方法是一个本地的方法: private native void start0();看不到start()方法调用run()方法了,所以它是交给底层C语言去实现了,底层CPU帮我们启动一个线程,并且帮我们分配线程资源,所以线程一旦启动我们是没有办法将其关闭等操纵它的。

4. 查看Thread类的run()

    public void run() {if (target != null) {    target.run();}    }
    private Runnable target;
target是Runnable对象,它是通过Thread的构造方法给其赋值的,

    public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0);    }
查看init方法中target的操纵

this.target = target;
所以可以确定它是target是通过 new Thread(Runnable target) 的方式传递过来的。

当我们使用第一种生成线程对象方式的时候,它里面所维护的Runnable对象是为空的,此时run()中的if()语句是不执行的,所以想要启动线程我们必须重写run()方法,所以当我们使用第二种生成线程对象的方式的时候,它里面所维护的就是我们传过去的Runnable对象。此时if语句执行,调用了run()方法。

5. 总结

1) Thread类也实现了Runnable接口,因此实现了Runnable接口中的run方法; 

2) 当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:Thread-number,该number将是自动增加的,并被所有的Thread对象所共享(因为它是static的成员变量)。 

3) 当使用第一种方式来生成线程对象时,我们需要重写run方法,因为Thread类的run方法此时什么事情也不做。 

4) 当使用第二种方式来生成线程对象时,我们需要实现Runnable接口的run方法,然后使用new Thread(new MyThread())(假如MyThread已经实现了Runnable接口)来生成线程对象,这时的线程对象的run方法就会调用MyThread类的run方法,这样我们自己编写的run方法就执行了。 

6. 掌握了以上的知识,现在我们接下来就来研究一下线程中的一些特性,线程的特性主要是由于线程的不确定性引起的:一旦线程启动,则它能操纵和停止它




未完待续,持续更新中......

































原创粉丝点击