關於Java線程中this.getName()和Thread.currentThread().getName()區別的深入分析

本文爲本人原創,轉載請標明出處。

先看測試代碼和運行結果:

問題的焦點在於運行結果標註的三個位置,爲什麼會是這三種結果?

分析:

逐個來看,先看運行結果1爲什麼是Thread-0。首先,子類的構造函數如果沒有顯式地指定調用父類的哪個構造方法,則在子類構造方法的第一行默認調用父類無參構造器,即隱式地調用super();其次,實例化子類要先實例化父類。本例中MyThread7是Thread的子類,而子類中沒有重寫父類的getName()方法(事實上,Thread類的getName()方法被final修飾不能被重寫),所以在代碼第13行的this.getName()調用的是父類的方法。前面已提到在實例化子類之前要先實例化父類,也就是說對於本例會先調用父類的默認構造器:

nextThreadNum()的源碼爲:

可見線程默認的名字是這麼來的。然後我們看一下Thread默認構造器裏面的init()方法:

在這裏可以看到在init()方法裏面給成員變量name進行賦值:

至此,父類Thread對象初始化完畢(父類的name屬性已經爲Thread-0),開始進行子類初始化工作,即運行MyThread7構造器後面的代碼。所以,到了代碼的第13行打印出的名字是父類剛剛初始化的名字Thread-0。

再來看運行結果的第2處和第3處,這裏有點不好理解——爲什麼打印的名字不一樣?而對於這個問題的關鍵在於對Thread.currentThread()和代理模式的理解。先來看Thread.currentThread(),這個很簡單,代表的是運行本行代碼的線程或者說本行代碼是運行在哪個線程裏面。第二個就是代理模式,爲什麼是代理模式呢?來看一下Thread類的run()方法:

target是什麼?接着看:

在本示例代碼中target也就是第35行作爲參數傳遞給Thread構造器的myThread7對象。當運行37行的thread.start()之後(獲取CPU時間片這些細節忽略),調用的是thread對象的run()方法,而在run()方法內部target不爲空,所以調用的是myThread7對象的run()方法,而MyThread7已經重寫了run()方法。那麼關鍵的地方到了:第19行的Thread.currentThread()到底是誰呢?這裏再強調一下Thread.currentThread()的理解:調用本方法的線程是誰,或者說本方法是在哪個線程裏面被調用的。不要在MyThread7類重寫的run()方法裏面找答案,否則就陷入了“不識廬山真面目,只緣身在此山中”的錯誤,而要到更高的層面去看,要到這裏來找答案:

所以清楚了,調用myThread7線程run()方法的是thread線程,也就是說第19行的Thread.currentThread()指的是thread線程,而在36行設置了線程的名字爲aaa,所以打印結果的第2處爲aaa。接着來看第3處,這裏就是對this指針的理解了,毋庸置疑,this指針指代的是調用本方法的對象是誰,前面已經分析過最終調用的是myThread7對象的run()方法,所以這裏的this指代的是myThread7。前面已經分析過myThread7沒有也不能複寫父類Thread的getName()方法,所以this.getName()的值就是上面最開始分析的父類默認的線程名稱Thread-0。再來看一下Thread類的run()方法:

我們可以打開實例代碼的第20和22行代碼來進行驗證:

 

 

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