目录
- 前言
- 继承Thread方式
- 实现Runnable接口
- 实现callable接口
- 总结
前言
看到这个标题的小伙伴先别着急喷我……在面试的时候,我们经常会被问到这种基础题:Java创建线程的方式有几种?
比较正常的答法当然是三种:
- 继承Thread
- 实现Runnable接口
- 实现callable接口
一般来说这种属于送分题,不过大家都是这样答的,好像有点一般般,有没有什么答法能够让面试官眼前一亮呢?当然有!
事实上,这个问题可以从两个角度去思考:
- Java层次
- 操作系统层次
大部分人的回答都是从Java提供的API层次,所以得出了三种,甚至还有加上线程池四种的结论,但你有没有想过,Java本身是不具备在操作系统上创建线程能力的。
更深入点思考:
Java提供的API分别是怎么创建线程的?
继承Thread方式
第一种方式,在构造方法实际上只是做了一些状态的初始化, 并没有涉及线程的创建,关键点其实是在Thread类的start()方法:
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) {}}}
阅读这段代码,让你最有印象的肯定是这个start0方法,名字起得这么奇怪,可惜点进去会发现它是一个native方法,我们暂时先跟到这里。
实现Runnable接口
runnable是一个接口,代码十分简单:
@FunctionalInterface
public interface Runnable {/*** When an object implementing interface <code>Runnable</code> is used* to create a thread, starting the thread causes the object's* <code>run</code> method to be called in that separately executing* thread.* <p>* The general contract of the method <code>run</code> is that it may* take any action whatsoever.** @see java.lang.Thread#run()*/public abstract void run();
}
思考下我们平时怎么用Runnable的?是不是实现这个接口,重写run方法,然后扔到Thread类里去执行?实际上我们继承Thread类也是要重写run方法,OK,我们再看看第三个。
实现callable接口
callable的情况好像有点不一样,它没有我们熟悉的run方法了:
@FunctionalInterface
public interface Callable<V> {V call() throws Exception;
}
然而真的没有吗?我们再来想想callable的用法,好像还有个FutureTask类没有出现,我们点进来就会发现,这个类居然是Runnable的实现类!

不用想了,直接看看run方法的逻辑,果然在这里有调用call()方法,那么是实际上我们还是可以认为是run方法的效果。

总结
从上面三个方式的分析,我们最终可以粗略得出来一个结论:
无论是继承Thread类还是实现接口,最终都是通过Thread类的start方法创建线程,由run方法执行逻辑
也就是说,对于Java提供的API来说,创建线程的方式实际上只有一种,也就是刚才得出来的结论。
到这里我们已经得到可以让面试官眼前一亮的答案了,那么这个native方法的具体逻辑是怎么实现的呢?这里我就不深入分析了,大家可以去找找相关博文,大概是创建线程后得到时间片时会由一个回调的处理来执行run方法的逻辑