建立線程的幾種方式:java
1. 經過繼承 Thread 類ide
public static class MyThreadOne extends Thread{ @Override public void run() { System.out.println("MyThreadOne running ... "); } }
2. 經過實現Runnable接口this
public static class MyThreadTwo implements Runnable{ @Override public void run() { System.out.println("myThreadTwo running ... "); } }
3. 經過Callable接口和FutureTask實現有返回值的線程spa
public static class MyThreadThree implements Callable<String>{ @Override public String call() throws Exception { System.out.println("MyThreadThree running .... "); return "success"; } }
具體的線程執行線程
public static void main(String[] args) { MyThreadOne myThreadOne = new MyThreadOne(); myThreadOne.start(); Thread thread = new Thread(new MyThreadTwo()); thread.start(); // futureTask 類實現了 RunnableFuture 接口 該接口繼承了 Runable 接口 繼承runnable 接口 就能經過 new Thread(new Runable())方式 // 經過線程執行 futureTask 中的 run 方法 ; 繼承 future 接口 能經過 get 方法 阻塞線程 獲取放回直 FutureTask<String> stringFutureTask = new FutureTask<>(new MyThreadThree()); new Thread(stringFutureTask).start(); try { // get 時經過 LockSupport.park() 阻塞線程 String s = stringFutureTask.get(); System.out.println(s); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } }
原理探究:debug
1. 關於第三種方式是如何實現線程執行 實現 Callable 接口中的 call 方法的?blog
經過debug 一步一步查看執行過程能夠發現繼承
thread.start方法建立線程,當線程被CPU分配時間片時, Runable實現類中的 run 方法,由於 FutureTask 類實現了 RunnableFuture 接口 該接口繼承了 Runnable 和 Future接口接口
實際上執行的是FutureTask 中的 run 方法,run 方法中執行 Callable接口中run 方法 也就是 本身實現類中的run 方法;rem
執行流程:
FutureTask<String> stringFutureTask = new FutureTask<>(new MyThreadThree()); new Thread(stringFutureTask).start();
線程啓動:
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 */ } } }
當線程分配CPU 時間後執行 run 方法:
/* What will be run. */
private Runnable target;
public void run() { if (target != null) { target.run(); } }
RunableFuture 繼承 Runnable 接口 和 Future 接口 ,而 FutureTask s實現 Runnable接口,執行 FutureTask 中的 run 方法
public class FutureTask<V> implements RunnableFuture<V> { 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 {
// 執行 Callale 實現類中的 call方法 result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } 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); } } }
該方法會調用 Callable 的接口的實現類,也就是咱們本身的Callable 接口實現類中的call方法
2. get方法是如何獲取到 線程的返回值的?
能夠發如今執行FutureTask 中的 run 方法中,在執行Callable 的call 放回 會獲取到 返回值 放到 FutureTask 的局部變量 outcoome 中,而後在經過 get方法當線程執行完成時,獲取該臨時變量的值返回;
執行 run 方法,執行完成後執行set 方法
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(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } 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); } }
set方法將方法的返回值放到 全局變量outcome 中
protected void set(V v) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = v; UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state finishCompletion(); } }
當執行get方法時
public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); }
當線程執行完成 會執行report 方法 從裏面拿到返回值
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); }
3. get方法爲啥會阻塞線程?
執行get方法時候,會判斷線程的狀態,若call方法還未執行完成,會執行 awaiter方法,該方法會經過循環判斷線程狀態,若線程狀態被中斷,則經過異常放回
若線程執行完成,則結束循環返回,若爲其餘狀態,經過 LockSupport.park()加鎖
public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); }
執行awaitNode
private int awaitDone(boolean timed, long nanos) throws InterruptedException { final long deadline = timed ? System.nanoTime() + nanos : 0L; WaitNode q = null; boolean queued = false; for (;;) { if (Thread.interrupted()) { removeWaiter(q); throw new InterruptedException(); } int s = state; if (s > COMPLETING) { if (q != null) q.thread = null; return s; } else if (s == COMPLETING) // cannot time out yet Thread.yield(); else if (q == null) q = new WaitNode(); else if (!queued) queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q); else if (timed) { nanos = deadline - System.nanoTime(); if (nanos <= 0L) { removeWaiter(q); return state; } LockSupport.parkNanos(this, nanos); } else LockSupport.park(this); } }
萬事預則立,不預則廢~~~加油