实现线程为什么Thread和Runnable 都可以

在最近的几次面试实习生有时候会问,实现线程的两种方式以及用哪个更好。
同学们一般都会答出extends Threadimplement Runnable ,至于哪种更好,大概会有半数人答不出来,虽然这个问题很简单,但是在实际工作中自己去生成一个线程的情况还是很少遇见的。但这里面的问题稍微抽象下就可以看出是考察extendsimplements之间的区别和优劣。
一般来说我们会选择implement Runnable 方式,因为Java是单继承但可以多个扩展,使用接口更利于扩展,另外扩展Runnable接口更利于资源共享,即多线程处理同一资源.
面试的话到这里可能就结束了,但是深入探究下,为什么两者都可以,两者之间的关系是怎么样的。
先写两个方法的简单示例:
Thread方式

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ThreadTest extends Thread {
private static final int _N = 10;

public void run() {
System.out.println(Thread.currentThread().getName() + " : " + _N);
}

public static void main(String[] args) throws InterruptedException {
ThreadTest t0 = new ThreadTest();
t0.start();
}

}

Runnable方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ThreadTest implements Runnable {
private static final int _N = 10;

public static void main(String[] args) throws InterruptedException {
ThreadTest tt = new ThreadTest();
Thread t0 = new Thread(tt);
t0.start();
}

/**
* 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 Thread#run()
*/
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " : " + _N);
}
}

再看下线程的几个状态之间的关系图:

在调用start()方法之前:线程处于NEW状态中,NEW状态指有一个Thread对象,但还没有一个真正的线程。
而在调用start()方法之后,才会使线程处理就绪状态,当该线程获得机会时会调用run().

回到前面的那两个例子,我们看Thread类的JDK源码,发现其中有个构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 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);
}

可以看出第二种Runnable方式就是Thread类也是可以通过启动Runnable来生成线程。
继续看下Threadstart()方法的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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();

/* 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);

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();

@Override
public void run() {
if (target != null) {
target.run();
}
}

其中start0()native方法实现。start线程还是要靠高效的本地函数啊。

注意: 对Java来说,run()方法没有任何特别之处。像main()方法一样,它只是新线程知道调用的方法名称(和签名)。因此,在Runnable上或者Thread上调用run方法是合法的。但并不启动新的线程。

参考链接: