Java線程的三種創建方式及源碼分析

線程的創建主要有三種方式

Thread直接創建

Thread類中有run()方法,每次線程調用start()時,線程啓動並開始執行run()方法。因此可以通過重寫Thread類中的run()方法來實現線程。

Thread thread = new Thread(){
    @Override
    public void run() {
        System.out.println("t1 run...");
    }
};
thread.setName("t1");
thread.start();

System.out.println("main run...");

Runnable接口繼承實現線程

Runnable runnable = () -> System.out.println("t1 run...");

new Thread(runnable, "t1").start();

System.out.println("main run....");

Runnable接口實現線程源碼查看

首先,根據上述代碼可以看到,在main()方法中通過向Thread類傳入繼承了Runnable接口的類來實現線程,然後根據這個,找到在Thread類中運行的代碼

public Thread(Runnable target, String name) {
    this(null, target, name, 0);
}

通過Thread類中的源碼發現,在該構造方法中調用了另一個構造方法,其中Runnable對象被傳入到另一個構造方法的第二個參數的位置。然後追蹤該被調用的構造方法,如下:

public Thread(ThreadGroup group, Runnable target, String name,
              long stackSize) {
    this(group, target, name, stackSize, null, true);
}

依然可以看到該構造方法又調用了另一個構造方法,而Runnable對象依然在被調用的構造方法的第二個參數位置。繼續追蹤被調用的構造方法,如下:

private Thread(ThreadGroup g, Runnable target, String name,
               long stackSize, AccessControlContext acc,
               boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;

    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        /* Determine if it's an applet or not */

        /* If there is a security manager, ask the security manager
               what to do. */
        if (security != null) {
            g = security.getThreadGroup();
        }

        /* If the security manager doesn't have a strong opinion
               on the matter, use the parent thread group. */
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }

    /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
    g.checkAccess();

    /*
         * Do we have the required permissions?
         */
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(
                SecurityConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }

    g.addUnstarted();

    this.group = g;
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext =
        acc != null ? acc : AccessController.getContext();
    this.target = target;
    setPriority(priority);
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
        ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;

    /* Set thread ID */
    this.tid = nextThreadID();
}

在上述代碼的 第53行可以看到這樣一行代碼:this.target = target;,由此可知,該構造方法中將Runnable的對象賦給了類中的屬性。然後通過搜索Thread類中的target屬性,發現了Thread類中的run()方法(其中線程創建時,當Thread類的start()方法調用後,會自動執行Thread類中的run()方法),而run()方法的源碼如下:

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

通過上述代碼可以看出,在Thread類中的run()方法中,首先判斷target是否存在,即Runnable對象是否存在,如果存在,則調用Runnable對象的run()方法,而Runnable接口的源碼如下:

@FunctionalInterface
public interface Runnable {
    
    public abstract void run();
}

因此,通過Runnable接口創建線程時,需要實現Runnable接口中的Run()方法。雖然在線程創建的時候執行的是Thread類中的run()方法,但實際調用的還是Runnable接口實現的run()方法。

FutureTask類實現

FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        System.out.println("t1 run ....");
        return 100;
    }
});

new Thread(task, "t1s").start();

int num = task.get();

System.out.println("main run" + num);

注意:

  1. 其中FutureTask類實現的線程方式是可以獲取該線程的返回值的,獲取方式如上代碼通過task.get()方式。如果FutureTask創建的線程需要獲取返回值,但線程未運行完,即線程還未返回值時,以上述代碼爲例,其中main線程會在獲取返回值的位置,即上述代碼中的task.get()處等待,當main線程獲取到返回值時,才繼續向下運行。
  2. 其中FutureTask方式創建的線程有返回值,而ThreadRunnable方式創建的線程沒有返回值。

FutureTask線程實現源碼查看

同樣,線程的實現都需要調用Thread類,根據這個追蹤Thread類的構造函數。源碼如下:

public Thread(Runnable target, String name) {
    this(null, target, name, 0);
}

看到這的時候發現通過FutureTask實現線程的方式與通過Runnable接口實現線程的方式在Thread類中的調用方式相同。但在上述Thread類的構造方法中傳入的是Runnable接口實現的對象,而在main方法中又可以看出在Thread類中傳入的是FutureTask類。

FutureTask類與Runnable接口是什麼關係呢?根據這個疑問查看FutureTask類的源碼:

public class FutureTask<V> implements RunnableFuture<V> {
    // ......
}

根據上述代碼,可以看到FutureTask類並沒有實現Runnable接口,但實現了RunnableFuture接口。(猜想:RunableFuture接口繼承了Runnable接口),根據這個,再查看RunnableFuture接口的源碼, 如下:

public interface RunnableFuture<V> extends Runnable, Future<V> {

    void run();
}

根據上面的代碼是可以確認RunnableFuture接口是繼承Runnable接口的。

通過上面的分析,可以知道爲什麼FutureTask類的實例可以傳入到Thread類的實例中。但在通過FutureTask類實現的線程中,實現了Callable接口並通過實現call()方法來實現該線程的邏輯,這是什麼原因?FutureTask類實現的線程又是如何返回值的。

通過之前的源碼分析,可以知道,Thread中的run()方法調用的實際上是Runnable接口的實現類的run()方法。同時FutureTask類實現了繼承Runnable接口的RunnableFutrue接口,那麼FutureTask必然實現run()方法,接下來查看FutureTask類中的run()方法實現,源碼如下:

public void run() {
    if (state != NEW ||
        !RUNNER.compareAndSet(this, 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);
    }
}

通過上面代碼的第6行:Callable<V> c = callable;可以看到將FutureTask類中的屬性callable賦給了c,並且在11行調用了call()方法,並返回了結果給result,第19行又執行了set(result)代碼。由此產生以下幾個疑問:

  1. FutureTask類中的callable屬性的值或對象的實例是如何傳進來的?(聯想main()方法中線程實現時在FuturtTask類內傳入了實現的Callable接口的實例)。
  2. FutureTaskrun()方法並沒有返回值,那通過FutureTask類實現的線程又是如何返回值的?(聯想main()方法中獲取線程的返回值是通過FutureTask類的實例調用get()方法)

首先解決第一個疑問,callable屬性是如何獲得值的。在main()方法中傳入了Callable接口的實例,根據這個查看FutureTask的源碼,如下:

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

如代碼所示,FutureTask類直接通過構造方法將Callable類的實例傳給callable屬性。

解決第二個問題,如何將線程的值返回。在FutureTask類中的run()方法有三行代碼Callable<V> c = callable;result = c.call();set(result);,再根據main()方法獲取線程的返回值是通過get()方法,可以看出result就是返回值,call()Callable接口的方法。其中Callable接口源碼如下:

@FunctionalInterface
public interface Callable<V> {

    V call() throws Exception;
}

set(result)代碼是將返回值結果存儲到了FutureTask的屬性中,可以追蹤FutureTask類中set()方法的源碼,如下:

protected void set(V v) {
    if (STATE.compareAndSet(this, NEW, COMPLETING)) {
        outcome = v;
        STATE.setRelease(this, NORMAL); // final state
        finishCompletion();
    }
}

可以看到,set()方法中將返回值賦給了FutureTask類的屬性outcome。聯想到main()方法中通過get()方法獲取線程返回值。然後查看FutureTask類中的get()方法,代碼如下:

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    return report(s);
}

可以看到最終返回的是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);
}

可以看到,方法中將outcome屬性的值賦給x, 然後將x返回出去。最總在main()方法中獲得返回的值。

FutureTask的類圖如下:

在這裏插入圖片描述

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