this與Thread.currentThread()的區別

引言

首先來看下下面這段代碼。這是一段簡單的中斷線程的示例代碼。

public class Test {

    static class Runner extends Thread {
        @Override
        public void run() {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("Interruted!");
                    break;
                }
            }
        }
    }
    
    public static void main(String[] args) {
        Runner runner = new Runner();
        runner.start();
        runner.interrupt();
    }

}

其中的 “Thread.currentThread()” 引發了我的思考:“既然該Runner的對象正在作爲線程運行,那 this 和 Thread.currentThread() 不也就是同一個對象,爲什麼不直接用this呢?,相比之下this更加簡短。”

在上面這段段代碼中,將 Thread.currentThread() 替換成 this,是等同的,運行結果相同,能正常退出。

而下面代碼,this 和 Thread.currentThread() 則運行結果則不同。this 會出現死循環,無法退出;Thread.currentThread() 能正常退出。

public class Test {

    static class Runner extends Thread {
        @Override
        public void run() {
            while (true) {
                if (this.isInterrupted()) {
                    System.out.println("Interruted!");
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        Runner runner = new Runner();
        Thread thread = new Thread(runner);
        thread.start();
        thread.interrupt();
    }

}

這說明 this 和 Thread.currentThread() 並不能完全等同。

其原因主要跟 Thread 類的內部實現有關。

正文

在直接揭祕兩者區別之前,先來了解下 Thread 類的內部實現。

Runner的中實現了構造函數,main方法中只創建了 runner 實例,並沒有啓動線程。

public class Test {
    static class Runner extends Thread {
        public Runner() {
            System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
            System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive());
            System.out.println("this.getName=" + this.getName());
            System.out.println("this.isAlive()=" + this.isAlive());
        }
    }

    public static void main(String[] args) {
        Runner runner = new Runner();
    }
}

/** output
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=true
this.getName=Thread-0
this.isAlive()=false
**/

根據前兩行輸出,可以看出,“當前的執行線程是 main 線程,並且處於運行狀態”,容易理解。

再看後兩行輸出,this 指向的是新建的 runner 實例,該實例的 name 屬性爲 “Thread-0”,並且沒有運行。沒有運行很容易理解,因爲沒有執行 runner.start() 方法。關於 name 屬性爲何是 “Thread-0”,這是在創建 Thread 實例時,初始化的名字。

下面是 Thread類 的構造函數,其中 init() 方法第三個參數爲線程的默認名字。生成名稱的規則是:“Thread-”加上創建的線程的個數(第幾個)。默認從0開始,由於main線程是默認就有的,所以並不計數 。

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}

所以 runner.getname() 得到的結果是 “Thread-0”。


接下來再看一段代碼。新建 Runner 實例對象 runner。由於 Runner 是 Thread 的子類,並且 Thread 實現了 Runnable 接口,所以可以作爲 Thread 類的構造函數參數傳入。將 runner 作爲構造函數參數傳入,創建 Thread 實例對象 thread,啓動 thread。

public class Test {
    static class Runner extends Thread {
        @Override
        public void run() {
            System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
            System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive());
            System.out.println("this.getName=" + this.getName());
            System.out.println("this.isAlive()=" + this.isAlive());
        }
    }

    public static void main(String[] args) {
        Runner runner = new Runner();
        Thread thread = new Thread(runner);
        thread.start();
    }
}

/** output
Thread.currentThread().getName()=Thread-1
Thread.currentThread().isAlive()=true
this.getName=Thread-0
this.isAlive()=false
**/

首先看下前兩行輸出,可看出 Thread.currentThread() 指向 thread 對象,其name爲“Thread-1”(第二個被創建),並且處於運行狀態。

再來看看後兩行輸出,可看出 this 指向的是 runner 對象,其name爲“Thread-0”,並且沒有運行。

爲什麼 this 不是指向 thread 呢??? 不是 thread 在運行嗎???

其原因在於 Thread thread = new Thread(runner)將 runner 對象綁定到 thread 對象的一個 pravite 變量target 上,在 thread 被執行的時候即 thread.run() 被調用的時候,它會調用 target.run() 方法,也就是說它是直接調用 runner 對象的run方法。

再確切的說,在run方法被執行的時候,this.getName() 實際上返回的是 target.getName(),而Thread.currentThread().getName() 實際上是 thread.getName()。

public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

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

總結

正在執行線程中的 this 對象並非就一定是該執行線程,因爲執行線程的 run方法 可能是直接調用 其他Runnabel對象的 run() 方法。

個人看法,不要在線程中用 this 來表示當前運行線程,不可靠。

參考文獻

  1. Java多線程之this與Thread.currentThread()的區別——java多線程編程核心技術

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