JAVA 创建线程的几种方式 源码解析
-
继承Thread类
-
实现Runnable接口
-
Future + Callable
原理简介:任何一个线程的开启,就是调用Thread实例的start( ) 方法,然后JVM新起一个线程执行Thread实例的 run( ) 方法。
先研究start( ) 方法的源码
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0(); // 可以看到,start() 没有直接调用run()方法,而是调用了本地方法start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
//本地方法,功能是开启一个线程,并执行run()方法
private native void start0();
再研究run( ) 方法的源码
private Runnable target;
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
@Override
public void run() {
if (target != null) {
target.run(); //Runnable 实例不为Null,调用Runnable实例的 run()方法。
}
}
分析start()和 run()方法后,目前我们可以得出两种创建线程的方式,
一、继承Thread类,重写Thread 的run( )方法。
二、实现Runnable接口,重写Runnable的run()
附上代码
//继承Thread类
public class Test {
public static void main(String[] args) {
new MyThread().start();
}
private static class MyThread extends Thread{
@Override
public void run(){
System.out.println("继承Thread类,重写run()方法");
}
}
}
public class Test {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("实现Runnable接口,重写Runnable的run()");
}
}).start();
}
}
当然这两种方法创建线程都没有返回值,如果我们需要有返回值的线程,该如何创建呢?
由于Thread类没有带返回值创建线程的API,所以需要我们自己实现,但是好在JDK1.5 提供了 Future + Callable机制。
那么接下来我们需要研究,在Thread类不支持带返回值创建线程的情况下,Future + Callable机制如何实现。
为了方便大家理解,先贴上Future + Callable 的 API 使用
public class Test {
public static void main(String[] args) {
// 用 Future 包装 Callable接口,传递给Thread
final FutureTask<Integer> future = new FutureTask<>(new Callable<Integer>(){
@Override
public Integer call() throws Exception {
Thread.sleep(2000);
return 1;
}
});
final Thread thread = new Thread(future);
thread.start();
try {
System.out.println("开始获取结果...");
Integer result = future.get();// 阻塞,直到获取到结果 。
System.out.println("获取结果成功"+ result);
} catch (InterruptedException e) {
e.printStackTrace(); // thread被中断
} catch (ExecutionException e) {
e.printStackTrace(); // thread 内部业务逻辑抛出的异常
}
}
}
UML图 FutureTask
FutureTask 由于实现了 Runnable接口,所以可以用于构建 Thread对象,那么在开启线程时,将会执行FutureTask里的 run( )方法.
所以创建带有返回值线程的秘密就藏在 FutureTask类里
开搞!
先研究 他的run方法()
public class FutureTask<V> implements RunnableFuture<V> {
/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call(); //执行 Callable接口的 Call方法。
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex); // 将异常信息存储到outcome中
}
if (ran)
set(result); //将执行结果,放到成员变量 outcome 中
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v; // 将一个值,赋值给成员变量outcome
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
}
理一下逻辑,FutureTask 类 通过实现 Runnable接口,重写了run( ) 方法。在run( ) 方法中,调用 Callable实例的 call ( ) 方法,并将 call( ) 方法返回值存储到 的成员变量outcome中。如果出了异常,就将异常信息存储在outcome中。这样一来,线程的返回值和异常信息就有地方保存了。
接下来,我们研究如何把这个返回值取出来。 API是调用 Future 的get()方法。我们研究一下源码
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L); //阻塞,直到线程运行完
return report(s);
}
//判断是返回结果还是抛出异常
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
不用说大家也猜到了,获取返回值的时候阻塞,直到线程出结果,或者抛出异常
总结:
本质上线程的创建就两种方式,继承Thread类
和实现Runnable接口
。但是JDK1.5加入了Future + Callable
机制 该机制可以创建线程并获取返回值
。该机制底层还是通过实现Runnable接口创建线程。 实现原理是 使用FutureTask的成员变量outcome
作为中介,存储了线程运算的返回值
或者异常信息
。然后对外提供get() 方法。当调用get() 方法时会阻塞,直到抛出异常,超时,或者线程运行完毕。