Thread源码分析
我们可以看到平时我们通过实现Runnable接口和继承Thread来重写run方法,最终归结到了run方法的调用上。一个是重写,一个是调用接口的方法。
源码示例:
/* 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, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
......
this.target = target;
......
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
Oracle官方文档对创建线程的说明
- Java SE 8 API文档:
docs.oracle.com/javase/8/do… - 请查看java.lang.Thread的类说明文档。 a. 将类继承Thread类重写run方法
- 官方原话:There are two ways to create a new thread of execution. One is to declare a classto be a subclass of Thread. This subclass should override the runmethod of class Thread.
实现多线程的方法
实现多线程的官方正确的只有两种方法,但是显然使用方法1实现的更好
方法1:实现Runnable接口
/**
* 1.实现runnable接口
*/
public class RunnableStyle implements Runnable {
public static void main(String[] args) {
/**
* 3。调用runnable接口
*/
RunnableStyle target=new RunnableStyle();
Thread thread=new Thread(target);
thread.start();
}
/**
* 2.实现run接口
*/
@Override
public void run() {
log.info("运行了线程-{}",Thread.currentThread().getId());
}
}
方法2:继承Thread类
public class ThreadStyle extends Thread {
public static void main(String[] args) {
/**
* 调用ThreadStyle和启动
*/
ThreadStyle threadStyle=new ThreadStyle();
threadStyle.start();
}
/**
* 2.重写run方法,覆盖父类方法
*/
@Override
public void run() {
log.info("运行了线程-{}",Thread.currentThread().getId());
}
}
不推荐继承Thread类
继承Thread类是不推荐的,因为它有以下的一些缺点:
- 从代码架构角度:具体的任务(run方法)应该和“创建和运行线程的机制(Thread类)”解耦,用runnable对象可以实现解耦。
- 使用继承Thread的方式的话,那么每次想新建一个任务,只能新建一个独立的线程,而这样做的损耗会比较大(比如重头开始创建一个线程、执行完毕以后再销毁等。如果线程的实际工作内容,也就是run()函数里只是简单的打印一行文字的话,那么可能线程的实际工作内容还不如损耗来的大)。如果使用Runnable和线程池,就可以大大减小这样的损耗。
- 继承Thread类以后,由于Java语言不支持双继承,这样就无法再继承其他的类,限制了可扩展性。
两种方法的本质对比
方法一和方法二,也就是“实现Runnable接口并传入Thread类”和“继承Thread类然后重写run()”在实现多线程的本质上,并没有区别,都是最终调用了start()方法来新建线程。这两个方法的最主要区别在于
run()方法的内容来源:
@Override
public void run() {
if (target != null) {
target.run();
}
}
- 方法一:最终调用target.run(),(target传入的runnable的run方法);
- 方法二:run()整个都被重写
start方法的执行流程
- 检查线程状态,只有NEW状态下的线程才能继续,否则会抛出IllegalThreadStateException(在运行中或者已结束的线程,都不能再次启动,详见CantStartTwice10类)
- 被加入线程组
- 调用start0()方法启动线程
注意点:
start方法是被synchronized修饰的方法,可以保证线程安全;
由JVM创建的main方法线程和system组线程,并不会通过start来启动。