继承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 FutureTaskfuture = new FutureTask<>(new Callable (){ @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 FutureTaskimplements RunnableFuture { 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 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() 方法时会阻塞,直到抛出异常,超时,或者线程运行完毕。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)