源码分析继承Thread和实现Runnable来创建线程

创建线程的方式有三种,此处不再赘述,在另一篇文章中已经写过,此处从源码的角度来分析通过继承Thread和通过实现Runnable来创建线程的过程

1.调用thread.start()和thread.run()的区别

Thread.java类中的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法,就是让系统安排一个时间来调用Thread中的run()方法,也就是使得线程得到运行,启动线程,具有异步的执行效果。

如果调用thread.run()方法,就不是异步了,而是同步,那么此线程对象并不交给线程规划器来进行处理,而是由调用该方法的线程来调用run()方法

另外再多提一点:当开启多个线程时,执行start()方法的顺序并不代表线程启动的顺序,只是将这些线程置于就绪状态,一同等待CPU的时间片来临,然后根据具体的CPU调度算法选择这些线程中的一个执行,其他的继续等待。

public class Test {
    public static void main(String[] args){
        Mythread myth = new Mythread();
        Mythread myth1 = new Mythread();
        System.out.println("main thread="+Thread.currentThread().getName());
        myth.start();
        myth1.start();
        myth.run();
    }
}

class Mythread extends Thread {

    @Override
    public void run() {
        System.out.println("run="+Thread.currentThread().getName());
    }
}
执行结果:
main thread=main
run=main
run=Thread-0
run=Thread-1

从执行结果可知,myth.run()方法并没有另自开辟一个线程,而是直接利用main主线程去同步调用run()方法。而myth.start()以及myth1.start()方法均开辟了新的线程去执行run()方法。

2.通过继承Thread来创建线程
public class MythreadA extends Thread {

    @Override
    public void run() {
        System.out.println("run="+Thread.currentThread().getName());
    }
}
public class Test {
    public static void main(String[] args) {
        MythreadA mythread = new MythreadA();
        mythread.start();
        System.out.println("main="+Thread.currentThread().getName());
    }
}

==> new MythreadA();创建线程对象,MythreadA的无参构造器中会调用父类构造方法,然后做相关的初始化工作。
==> 主线程调用mythread.start();
==> start()方法将mythread添加到线程组,然后调用native方法 start0() (该方法的实现由非java语言实现,比如C。)
==> 线程mythread进入就绪等待CPU调度
==> CPU给mythread分配时间片
==> start0()调用mythread.run()
主线程main和线程mythread实现了并发。

JDK中Thread的部分源码如下:

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

 public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

3.通过实现Runnable接口来创建线程
public class TestB {
    public static void main(String[] args){
        Runnable mythb = new MythreadB();
        Thread thread = new Thread(mythb);
        thread.start();
    }

}
class MythreadB implements Runnable {

    @Override
    public void run() {
        System.out.println("run="+Thread.currentThread().getName());
    }
}

==> Runnable mythb = new MythreadB();
==> Thread thread = new Thread(mythb);
==> thread.start();
==> thread.start0();–>等待CPU
==> thread.run()
==> mythb.run()
主线程main和thread实现并发

Thread.java中有构造方法 Thread(Runnable target)
调用此构造方法后,会对target做一些初始化工作,包括线程名称、线程组等的分配。其中会执行一条代码 this.target = target;将Thread的target成员变量赋值为传入的Runnable类型的参数target。

执行thread.run()方法时会先检查当前Thread的target是否为null,如果不为空,则执行target.start()(此处不同于使用继承的方式,由于继承时子类MythreadA重写了Thread的 run()方法,所以线程得到CPU时间片后native方法 start0()直接调用重写后的MythreadA的run()方法,由于此处并没有重写thread的run()方法,所以将使用Thread.java中的run()方法)

JDK部分源码

/* What will be run. */
private Runnable target;

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

private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
        init(g, target, name, stackSize, null);
    }

private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc) {

        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name.toCharArray();
    ......(代码过长只截取了其中几个片段,详细代码可自行查看JDK中的源码)
        Thread parent = currentThread();
    ......
        this.target = target;
    ......
    }

 @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
4.两种方式在并发上的具体细节

在上面的条目2中,主线程main和创建的线程(MythreadA)thread并发执行,输出方法System.out.println(“run=”+Thread.currentThread().getName()); 在MythreadA中。MythreadA extends Thread

条目3中,主线程main和创建的线程(Thread)thread并发执行,输出方法System.out.println(“run=”+Thread.currentThread().getName()); 在MythreadB中。MythreadB implements Runnable

总结:通过继承Thread的线程(MythreadA)是直接与main线程并发执行,而通过实现Runnable方法的线程(MythreadB)是靠新建一个Thread对象,用该对象调用MythreadB的run()方法从而使得MythreadB与主线程main并发执行,然而,新创建的Thread与MythreadB却是同步执行的(在Thread.run()中调用的是target.run(),条目1中已经证明两者属于同步调用),新建的Thread起一个辅助作用,它的run()方法除了调用target的run()之外没有什么其他的操作。

5.如何将一个Thread对象中的run()方法交由其他的线程进行调用

此处用一个简单的例子对条目4中最后的粗体部分进行说明
有三个线程类Parent,child1,child2,

package Chapter1;

public class Parent extends Thread {

    public Parent(Runnable target) {
        super(target);
    }

    @Override
    public void run() {
        super.run();
        System.out.println("Parent run="+Thread.currentThread().getName());
    }

    public static void main(String[] args){
        System.out.println("main="+Thread.currentThread().getName());
        Parent mother = new Parent(new child2());
        Parent father = new Parent(new child1());
        mother.start();
        father.start();
    }

}

class child1 extends Thread{

    @Override
    public void run() {
        System.out.println("child1 run="+Thread.currentThread().getName());
    }

}

class child2 implements Runnable{

    @Override
    public void run() {
        System.out.println("child2 run="+Thread.currentThread().getName());
    }

}
输出结果:
main=main
child2 run=Thread-0
child1 run=Thread-2
Parent run=Thread-0
Parent run=Thread-2

注意:在Parent的run()方法中显式的调用了父类的run()方法从而调用传入参数target的 target.run()。
由输出结果可知一个存在三个线程:main、thread-0、thread-2
Parent run=Thread-0 <===> child2 run=Thread-0 同步
Parent run=Thread-2 <===> child1 run=Thread-2 同步
根据这个例子,进一步说明了条目4的最后一段话的具体过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值