Thread、Runable、Callable實現多線程

多線程的實現方式:

多線程的實現方式,目前有4中: Thread、Runnable、Callable、線程池【本文不介紹】

  1. Thread類 :通過繼承Thread類,並重寫其中的Run()方法,調用Thread.start() 方法啓動線程
  2. Runnable接口:實現Runnable接口,並且完成Run()方法,仍需借用Thread.start()方法啓動線程
  3. Callable接口: 通過實現接口,重寫call()方法【有返回值】,通過 FutureTask裝飾,最終也是需要Thread.start()方法啓動線程

代碼示例:

  1. 繼承Thread :
public class ThreadTest extends Thread{

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

    @Override
    public void run() {
        System.out.println("I am Thread! ");
    }
}

運行結果:

I am Thread! 
  1. 實現Runnable 接口 :
public class RunnableTest implements Runnable {

    public static void main(String[] args) {
        RunnableTest runnableTest = new RunnableTest();
        new Thread(runnableTest).start();
    }

    @Override
    public void run() {
        System.out.println("I am Runnable");
    }
}

運行結果:

I am Runnable
  1. Callable接口實現多線程需要藉助 FutureTask 類:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableTest implements Callable {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableTest callableTest = new CallableTest();
        FutureTask<String> task = new FutureTask<String>(callableTest);
        new Thread(task).start();
        System.out.println(task.get());

    }

    @Override
    public Object call() throws Exception {
        return "I am CallableTest !";
    }
}

運行結果:

I am CallableTest!

源碼解析:

關於Thread類 和 Runnable接口,我們可以看到:
  1. Runnable 中 :
public interface Runnable {
   
    // 定義了Run()方法,執行入口,業務邏輯均在此方法中實現
    public abstract void run();
    
  1. 在Thread中:
public  class Thread implements Runnable {

	// Runnable的實現類, 上面的實例代碼 runnableTest 對象
	private Runnable target;

  // 線程啓動方法
   public synchronized void start() {
       
       // 狀態校驗  0:NEW 新建狀態
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
            
         // 添加進線程組 -main 方法中, 後續調用
        group.add(this);

        boolean started = false;
        try {
           //調用native方法執行線程run方法
            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();

   // 當多線程的實現方式實現Runnable 接口時,通過 thread.run() 方法調用 runnableTest.run()方法
    public void run() {
        if (target != null) {
            target.run();
        }
    }
    
   // 省略代碼

}
  1. 我們可以看到不管是Thread 還是 Runnable 線程的真正實現都是有Thread.start() 方法啓用的。
    再調用 原生start0() , 回調到 run() 方法中【Runnable 通過thread.run() 方法調用 target.run(),實現我們的業務邏輯】
  2. Thread 和 Runnable 的區別就在於,Java 是單繼承 和多實現,使用Thread 類 難免會對我們的業務擴展產生影響。
  3. 面試中還有一個常問的問題:如果一個線程多次Start 後的結果 , 答案就在於 if (threadStatus != 0) throw new IllegalThreadStateException() 這兩代碼,每次啓動時都會判斷 當前 thread 的運行狀態,上次的start 會使得 threadStatus != 0 繼而拋出異常。
關於FutureTask類 和 Callable接口實現的多線程:
  1. Callable 接口
public interface Callable<V> {
   
   // 計算結果,如果無法執行,則引發異常
    V call() throws Exception;
}
  1. FutureTask 繼承關係:
    在這裏插入圖片描述
    FutureTask 部分源碼:
public class FutureTask<V> implements RunnableFuture<V> {


    // 實現 Runnable run() 方法
    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 {
                    // 執行 Call 方法,並獲得返回結果
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    // call 方法拋出異常
                    result = null;
                    ran = false;
                    setException(ex);
                }
                // 執行成功將 返回結果放到 outcome 中,在通過get()獲得
                if (ran)
                    set(result);
            }
        } finally {
            runner = null;
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

    // 等待線程結束,獲得線程的返回值 outcome
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            // 等待線程執行結束,或者執行異常
            s = awaitDone(false, 0L);
        return report(s);
    }

    // 等待線程執行結束,或者執行異常 ,並返回線程的狀態
    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);
        }
    }
}
  1. 結合 Callable 和 FutureTask 源碼不難發現, FutureTask 本身是實現了Runnable 接口,已經幫我們實現了多線程功能,我們定義的 CallableTest 作爲一個 變量 賦值 FutureTask#callable 參數中,
  2. 在執行 Thread#start() 方法中,會調用 FutureTask#run() 方法,再到我們的 callable.call() 方法,進入到我們的業務,並把返回值賦值給 outCome。
  3. 當我們通過 task.get() 回去返回值時,通過等待 線程執行結束,獲得 call() 的返回值並返回。
  4. 總而言之, FutureTask 是個已經寫好了的線程對象,我僅需吧 Callable交給他執行,即可

以上僅爲博主的簡單認識,多多評論交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章