源码分析继承Thread和实现Runnable来创建线程
2017-10-30 03:14
417 查看
创建线程的方式有三种,此处不再赘述,在另一篇文章中已经写过,此处从源码的角度来分析通过继承Thread和通过实现Runnable来创建线程的过程
如果调用thread.run()方法,就不是异步了,而是同步,那么此线程对象并不交给线程规划器来进行处理,而是由调用该方法的线程来调用run()方法
另外再多提一点:当开启多个线程时,执行start()方法的顺序并不代表线程启动的顺序,只是将这些线程置于就绪状态,一同等待CPU的时间片来临,然后根据具体的CPU调度算法选择这些线程中的一个执行,其他的继续等待。
从执行结果可知,myth.run()方法并没有另自开辟一个线程,而是直接利用main主线程去同步调用run()方法。而myth.start()以及myth1.start()方法均开辟了新的线程去执行run()方法。
==> new MythreadA();创建线程对象,MythreadA的无参构造器中会调用父类构造方法,然后做相关的初始化工作。
==> 主线程调用mythread.start();
==> start()方法将mythread添加到线程组,然后调用native方法 start0() (该方法的实现由非java语言实现,比如C。)
==> 线程mythread进入就绪等待CPU调度
==> CPU给mythread分配时间片
==> start0()调用mythread.run()
主线程main和线程mythread实现了并发。
JDK中Thread的部分源码如下:
==> 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部分源码
条目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()之外没有什么其他的操作。
有三个线程类Parent,child1,child2,
注意:在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的最后一段话的具体过程。
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【多线程知识总结(5)】比较继承Thread类创建线程和实现Runnable接口创建线程这两种方式
- 线程创建和启动:继承Thread类和实现Runnable接口
- Java【多线程知识总结(5)】比较继承Thread类创建线程和实现Runnable接口创建线程这两种方式
- java多线程学习1-继承Thread类和实现Runnable接口来创建线程
- Java中继承Thread和实现Runnable这两种创建线程有何区别
- Java中继承Thread和实现Runnable这两种创建线程有何区别
- 通过继承Thread类和通过实现Runnable接口 创建线程的区别
- 继承Thread类与实现Runnable接口创建线程对于类实例变量共享的区别
- 细说继承Thread类和实现Runnable接口来创建线程的区别
- 线程的创建:实现Runnable接口和继承Thread类,(倒计时的实现)
- 创建线程继承Thread和实现Runnable接口
- 线程 创建和启动线程的两种方式 实现Runnable接口 继承Thread类 重写唯一方法run()
- 线程:创建线程有两种方式,一种是继承Thread类,另一种是实现Runnable接口。代码如下:
- 在java编程时,线程创建实现Runnable接口与继承Thread类的不同
- 创建线程的两种方式:继承Thread类和实现Runnable接口
- 用继承thread或实现Runnable接口实现线程的区别
- Java中继承thread类与实现Runnable接口区别的简要的分析
- JAVA中创建线程对象的两种方法:继承Thread和实现Runable
- 【转载】JAVA中线程的两种实现方法-实现Runnable接口和继承Thread类
- 2-5-Java多线程-创建线程的Runnable接口方法及Thread源码解析