JavaSE第九十九讲:Thread类源码深度剖析
来源:互联网 发布:淘宝宠物医生 编辑:程序博客网 时间:2024/05/01 22:22
在上一讲内容中,我们已经学习了实现线程的两种方式,但是到底这两种方式有什么关系?为什么实现线程的两种方式中继承Thread类的那个类必须要重写run()方法?为什么用start()方法来启动线程?基于这些问题我们必须去研究线程Thread类的源代码,这样才能只其然且知其所以然,这一讲我们就来讲解Thread类源代码。
1.查看Thread类的源代码可以发现它也实现了Runnable接口publicclass Thread implements Runnable1) 因为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. 掌握了以上的知识,现在我们接下来就来研究一下线程中的一些特性,线程的特性主要是由于线程的不确定性引起的:一旦线程启动,则它能操纵和停止它
未完待续,持续更新中......
- JavaSE第九十九讲:Thread类源码深度剖析
- JavaSE第二十九讲:String类源代码深剖析
- JavaSE第三十讲:String类陷阱深度剖析
- JavaSE第四十五讲:hashCode与equals深度剖析与源码详解
- JavaSE 第六十二讲: Class类、Method类及Field类的使用方式深度剖析
- JavaSE第六十七讲:InvocationHandler接口与Proxy类深度剖析
- JavaSE第八十七讲:File类详解及使用陷阱深度剖析
- JavaSE第四十六讲:迭代器、TreeSet及Comparator深度剖析
- JavaSE第五十二讲:HashSet 与HashMap源代码深度剖析
- JavaSE第六十一讲:Java反射机制深度剖析
- JavaSE第六十五讲:静态代理模式深度剖析
- JavaSE第七十五讲:异常笔试、面试常见问题深度剖析
- JavaSE 第八十二讲:观察者模式深度剖析
- JavaSE第一百讲:线程同步问题深度剖析
- JavaSE第三十九讲:深入详解ArrayList
- JavaSE第四十九讲:Map.Entry详解
- JavaSE第六十九讲:Java Annotation详解
- JavaSE第十七讲:继承剖析
- PHP 用什么代替 echo exit 的调试方法
- 树莓派(raspberry pi)学习13: 树莓派真机上手,开机报告
- pyGame编程之初次pygame
- 开源数据库连接池Bonecp应用
- windows8中的界面网格的填写
- JavaSE第九十九讲:Thread类源码深度剖析
- Biography section of a journal
- windows8中实现界面图片的倒影效果
- highcharts报表插件之chart参数的使用
- Linux查看多核CPU利用率
- 算法导论学习笔记(十):约瑟夫排列
- 2013-1-26
- ubuntu下安装JDK并配置java环境
- Linux下kdesvn提交更改时提示Entry for 'xxx' is marked as 'copied' but is not itself sched