您的位置:首页 > 编程语言 > Java开发

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

2017-10-30 03:14 417 查看
创建线程的方式有三种,此处不再赘述,在另一篇文章中已经写过,此处从源码的角度来分析通过继承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时
4000
间片后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的最后一段话的具体过程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Java并发编程
相关文章推荐