Java併發編程之多線程基礎(二)

編程語言 Java 程序員 面試 碼農那些事 碼農那些事 2017-09-02

前兩天發了兩篇程序員面試經驗,分別是唯品會阿里巴巴的,從面試官問的問題上來看,他們都對多線程相關的問題情有獨鍾。因此,我會整理一份Java多線程的基礎知識分享給大家,由於知識點較多,會做成一個系列分成多篇來講。

阿里巴巴面試題,看看你能回答出多少?

最近很多人收藏了我發的這篇阿里面試經驗的文章,但最好是要關注我哦,並多來訪問下我的主頁,否則我更新了其他新的好文章,你可能就錯過了~

上一篇主要講了線程的創建,線程的6種狀態,以及如何中斷線程和讓線程休眠,這次接著繼續講。

一、等待線程結束(join)和謙讓(yield)

線程之間需要協作,比如一個線程a需要等待另一個線程b執行完畢才能繼續執行,在a線程中調用b.join()操作就是實現這個功能。當一個線程對象的無參join方法被調用時,調用它的線程將被掛起,直到這個線程對象完成它的任務。

join方法有3個重載方法,一個無參,一個帶long參數可以指定等待的毫秒數,還有一個帶2個long參數,可以指定等待的毫秒數和納秒數。無參版本表示無限等待,其實際是調用了long參數版本(該方法是synchronized的,通過調用wait實現),只是傳遞了參數0進去。當一個線程調用其他線程的join()方法時,如果帶有參數,則不必等到被調用線程運行終止,如果參數指定的時間已經到達,它將繼續運行。

join(long milliseconds); //指定毫秒

join(long milliseconds,long nanos); //指定毫秒和納秒

高階知識點:b.join()的本質是通過調用b.wait()讓線程a處於等待狀態,當線程b結束時,會調用this.notifyAll()通知所有等待的線程,從而使得調用線程a在線程b結束後能繼續執行。

while(isAlive()) {

wait(0);

}

yield()方法是Thread類的靜態方法,它會使當前線程讓出CPU。但讓出CPU不表示當前線程不執行了,當前線程在讓出CPU後,還會進行CPU資源的爭奪,但是否能再次被分配到則不一定。

二、守護線程

可以通過線程對象的setDaemon(true)方法設置線程為守護線程,不設置默認和父線程一樣,一般為非守護線程(即用戶線程)。設置守護線程必須在線程start()之前設置,否則會拋IllegalThreadStateException,但是程序和線程依然可以正常執行,只是被當成用戶線程而已。

三、為線程設置自定義的運行時異常handler

線程的run方法不支持throws語句,所以run方法向上拋異常時只會是uncheck的運行時異常,此時默認行為是把堆棧信息輸出到System.err並結束該線程.Java提供了一種在線程對象裡捕獲和處理運行時異常的機制.

Thread類有個UncaughtExceptionHandler接口,實現該類的uncaughtException方法可以自定義當run中出現運行時異常時的動作.線程對象的setUncaughtExceptionHandler方法可以為該線程設置異常handler,Thread類的靜態方法setDefaultUncaughtExceptionHandler可以為所有線程設置默認的異常handler.

四、ThreadLocal

如果創建的對象是實現了Runnable接口的類的實例,用它作為傳入參數創建多個線程對象並啟動這些線程,那麼所有的線程都將共享相同的屬性。即如果在一個線程中改變了一個屬性,所有線程都會被這個改變影響。將變量定義為ThreadLocal的則可以在每個線程中存在單獨不共享的該變量。

ThreadLocal變量一般定義為private static的。ThreadLocal類常用4個方法

void set(Object value):設置當前線程的線程局部變量的值。

public Object get():該方法返回當前線程所對應的線程局部變量。

public void remove():將當前線程局部變量的值刪除,目的是為了減少內存的佔用,JDK 5.0新增方法。需要指出的是,當線程結束後,對應該線程的局部變量將自動被垃圾回收,所以顯式調用該方法清除線程的局部變量並不是必須的操作,但它可以加快內存回收的速度。

protected Object initialValue():返回該線程局部變量的初始值,該方法是一個protected的方法,是為了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,在線程第1次調用get()或set(Object)時才執行,並且僅執行1次。ThreadLocal中的缺省實現直接返回一個null。

Java併發編程之多線程基礎(二)

面試常考點:兩個使用ThreadLocal的常見例子是SimpleDateFormat和Random類。SimpleDateFormat不是線程安全的,如果使用同步開銷太大。Random雖然是線程安全的(內部的seed是AtomicLong),但在多個線程中等待一個共享的隨機數生成器太低效(JDK 1.7提供了方便的ThreadLocalRandom類)。

五、線程組ThreadGroup

ThreadGroup類表示一組線程,線程組可以包含線程對象,也可以包含其他線程組對象,是一個樹形結構。

ThreadGroup threadGroup = new ThreadGroup("groupName");

Thread t = new Thread(threadGroup, runnableClass);

創建Thread時可以指定線程所屬的線程組,通過線程組可以對組中的線程進行統一操作,如中斷組中的所有線程。threadGroup提供的activeCount()方法可以獲取組中活躍的線程數,list()方法可以打印這個線程組中所有的線程信息(適合debug使用)。

和Thread類類似,可以通過繼承ThreadGroup類並覆蓋uncaughtException方法來自定義ThreadGroup的異常動作。當線程組中的任何線程在run方法中拋出unchecked異常時,這個方法就會被調用。

高階知識點:線程拋未捕獲異常時的完整邏輯:JVM先尋找拋出該異常的線程的未捕獲異常處理器(uncaughtException Handler);如果不存在,則JVM繼續尋找這個線程所在的線程組的未捕獲異常處理器;如果也不存在,JVM將尋找默認的未捕獲異常處理器。如果這些處理器都不存在,JVM將把堆棧信息輸出到System.err,並且結束該線程。

六、創建個性化的線程工廠類

要實現一個自己的線程工廠類,只需要implements了ThreadFactory接口,其只有一個newThread方法,它以Runnable接口對象作為參數並且返回一個線程對象。大多數基本的線程工廠類只有一行,即:return new Thread(r);

本文中,標記一些“高階知識點”和“面試常考點”,所謂“高階知識點”並不是說這個知識點很高級,主要想表達的意思是,如果你面試時能說出這些高階知識點,很可能會讓面試官眼前一亮,提高對你的評價。“面試常考點”就是面試時可能經常會被問到相關問題的知識點。

本篇暫時介紹這些,請期待下一篇。

本頭條號會不定期分享程序員的面試經驗,以及程序員需要掌握的技術乾貨,喜歡就關注我~

完。

相關推薦

推薦中...