先看Java开启Thread的两种写法
1. 继承Thread类,重写run方法。
class MyThread extends Thread{
@Override
public void run() {
}
}
void startThread(){
MyThread myThread = new MyThread();
myThread.start();
}
2. 实现Runable接口的run方法,并传入Thread的构造方法。
class MyTask implements Runnable{
@Override
public void run() {
}
}
void startThread(){
MyTask myThread = new MyTask();
Thread thread = new Thread(myThread);
thread.start();
}
很多人会有疑问,这两种方式开启线程在底层实现上有差别吗?
带着这个问题我们再来看JDK源码中Thread这个类。
public class Thread implements Runnable {
//省略2000多行代码...
}
Thread类本身实现了Runable接口。
继续查看JDK源码中的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类是怎么实现run方法的。
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
而这个target又是什么?这是Thread类的一个Runnable类型的成员变量,通过搜索发现只有在init()和exit()方法中对这个成员变量进行赋值。
这里先看init方法
/**
* Initializes a Thread.
*
* @param g the Thread group
* @param target the object whose run() method gets called
* @param name the name of the new Thread
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.
* @param acc the AccessControlContext to inherit, or
* AccessController.getContext() if 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;
Thread parent = currentThread();
if (g == null) {
if (security != null) {
g = security.getThreadGroup();
}
}
g.checkAccess();
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
this.target = target;
init2(parent);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
这个方法基本工作就是初始化各个成员变量,通过查找发现Thread的一系列构造方法最终都调用了这个方法,而以上第二种方法在Thread构造方法中传入的Runnable对象的引用最终就是赋值给了这个成员变量target,会在Thread的run方法中调用Runnable对象的run方法。
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, target, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*
* @param target
* the object whose {@code run} method is invoked when this thread
* is started. If {@code null}, this classes {@code run} method does
* nothing.
*/
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
通过分析init代码发现,在构建出一个Thread对象时,其实并没有真正在虚拟机内存空间中给这个线程分配内存。虚拟机真正创建线程是在调用start方法时,通过start方法源码可以观察到蛛丝马迹。
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
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".
*/
// Android-changed: Replace unused threadStatus field with started field.
// The threadStatus field is unused on Android.
if (started)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
// Android-changed: Use field instead of local variable.
// It is necessary to remember the state of this across calls to this method so that it
// can throw an IllegalThreadStateException if this method is called on an already
// started thread.
started = false;
try {
// Android-changed: Use Android specific nativeCreate() method to create/start thread.
// start0();
nativeCreate(this, stackSize, daemon);
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 */
}
}
}
// Android-changed: Use Android specific nativeCreate() method to create/start thread.
// The upstream native method start0() only takes a reference to this object and so must obtain
// the stack size and daemon status directly from the field whereas Android supplies the values
// explicitly on the method call.
// private native void start0();
private native static void nativeCreate(Thread t, long stackSize, boolean daemon);
这里关注nativeCreate这个方法,这是一个本地方法,具体实现不深入研究,最终通过start中的这个方法开启一个线程,而真正在线程中执行的代码就是run方法中根据具体业务实现的代码。
回答上面的问题:两种创建线程的方法,区别就是对run方法的处理,第一种方式通过直接继承重写来实现,第二种方式则通过传参组合的方式实现,对线程的最终执行并无本质区别。
任何线程都是Runnable类型,Runnable这个词用的相当到位,可以理解为可运行状态,但不一定是正在运行的,因为线程是CPU的最小调度单元,CPU的快速调度线程状态的切换对于用户几乎是无感知的。
关于操作系统线程的概念,可以阅读《操作系统精髓与设计原理》知识梳理 之线程篇。