多線程是Java技術面試中面試官比較喜歡問的問題之一。在這裏,從面試的角度列出了大部分重要的問題,可是做爲一個程序員仍然應該牢固的掌握Java多線程基礎知識來對應往後碰到的問題。java
1、wait和notify是成對出現的,他們爲何是object(而不是thread)的方法?
a、object除了是全部類的跟節點,它還引入了對象鎖的概念(暫且理解:標記一個對象的獨佔性標記)
b、wait是操做對象鎖的,這個syncrinized關鍵字是相同的。
c、相比下,thread的sleep等操做的是線程對象,thread表明的是一個行爲,object表明的是一個目標對象,二者是不一樣的概念。瞭解這點很重要。
1. 進程和線程之間有什麼不一樣?程序員
一個進程是一個獨立(self contained)的運行環境,它能夠被看做一個程序或者一個應用。而線程是在進程中執行的一個任務。Java運行環境是一個包含了不一樣的類和程序的單一進程。線程能夠被稱爲輕量級進程。線程須要較少的資源來建立和駐留在進程中,而且能夠共享進程中的資源。面試
a、常見的進程在系統「任務管理器」中,能夠看作是一個軟件或應用,線程能夠看作是一個輕量級的「進程」,常表明CPU具體處理的任務。 b、能夠理解爲:進程是多線程運行的一個環境,線程用於處理進程中的任務
2. 多線程編程的好處是什麼?編程
在多線程程序中,多個線程被併發的執行以提升程序的效率,CPU不會由於某個線程須要等待資源而進入空閒狀態。多個線程共享堆內存(heap memory),所以建立多個線程去執行一些任務會比建立多個進程更好。舉個例子,Servlets比CGI更好,是由於Servlets支持多線程而CGI不支持。windows
1、現實背景來講,如今的CPU都是多核的,能夠並行處理多個任務,多線程的出現能夠更好的發揮CPU的性能,提升資源利用率。 2、多線程將原來的一個任務分紅多個併發的任務來處理,減小了程序的響應,加強了用戶的體驗。 ps:相比並行的同一時間不一樣的核處理不一樣的一個事務,併發更高一級,同一時間,不一樣的核處理多個事務。
3. 用戶線程和守護線程有什麼區別?緩存
當咱們在Java程序中建立一個線程,它就被稱爲用戶線程。一個守護線程是在後臺執行而且不會阻止JVM終止的線程。當沒有用戶線程在運行的時候,JVM關閉程序而且退出。一個守護線程建立的子線程依然是守護線程。安全
1、用戶線程就是程序建立的用於處理具體任務的線程,守護線程是一種特殊的線程,不處理具體任務,只起輔助做用。 2、守護線程具體的實現就是調用setDaemon(true)來設置,如垃圾回收。 3、另外,守護線程建立的線程默認也是守護線程。 4、守護線程並不影響jvm的退出。也就是說jvm爲線程服務,但不爲守護線程服務。
ps:jvm退出就是銷燬jvm中的對象。
4. 咱們如何建立一個線程?多線程
有兩種建立線程的方法:一是實現Runnable接口,而後將它傳遞給Thread的構造函數,建立一個Thread對象;二是直接繼承Thread類。併發
1、有兩種,都和Thread有關。 2、實現runnable接口,傳遞給Thread 或 繼承Thread
5. 有哪些不一樣的線程生命週期?jvm
當咱們在Java程序中新建一個線程時,它的狀態是New。當咱們調用線程的start()方法時,狀態被改變爲Runnable。線程調度器會爲Runnable線程池中的線程分配CPU時間而且講它們的狀態改變爲Running。其餘的線程狀態還有Waiting,Blocked和Dead。
6. 能夠直接調用Thread類的run()方法麼?
固然能夠,可是若是咱們調用了Thread的run()方法,它的行爲就會和普通的方法同樣,爲了在新的線程中執行咱們的代碼,必須使用Thread.start()方法。
7. 如何讓正在運行的線程暫停一段時間?
咱們可使用Thread類的Sleep()方法讓線程暫停一段時間。須要注意的是,這並不會讓線程終止,一旦從休眠中喚醒線程,線程的狀態將會被改變爲Runnable,而且根據線程調度,它將獲得執行。
8. 你對線程優先級的理解是什麼?
每個線程都是有優先級的,通常來講,高優先級的線程在運行時會具備優先權,但這依賴於線程調度的實現,這個實現是和操做系統相關的(OS dependent)。咱們能夠定義線程的優先級,可是這並不能保證高優先級的線程會在低優先級的線程前執行。線程優先級是一個int變量(從1-10),1表明最低優先級,10表明最高優先級。
1、線程優先級表示,優先級高的線程在線程調度中擁有優先執行的權限。 2、java中優先級有1到10級。 3、java的這種優先級只是對系統的一種建議,具體的調度狀況依賴於操做系統。如windows中有個優先級推動器,執行次數多的任務優先級較高。
四、java中使用setPriority來設置。
9. 什麼是線程調度器(Thread Scheduler)和時間分片(Time Slicing)?
線程調度器是一個操做系統服務,它負責爲Runnable狀態的線程分配CPU時間。一旦咱們建立一個線程並啓動它,它的執行便依賴於線程調度器的實現。時間分片是指將可用的CPU時間分配給可用的Runnable線程的過程。分配CPU時間能夠基於線程優先級或者線程等待的時間。線程調度並不受到Java虛擬機控制,因此由應用程序來控制它是更好的選擇(也就是說不要讓你的程序依賴於線程的優先級)。
1、顧名思義,線程調度器是對多個就緒狀態的線程如何運行提供調解,也就是如何分配CPU時間。如多個線程,誰先運行,誰後運行,掛起誰等 2、線程調度策略有三種: a、時間片輪轉調度策略每一個進程分配一段運行時間,時間到後換成其餘線程 b、搶佔式調度策略:優先級高的先執行
三、時間分片是個動詞
10. 在多線程中,什麼是上下文切換(context-switching)?
上下文切換是存儲和恢復CPU狀態的過程,它使得線程執行可以從中斷點恢復執行。上下文切換是多任務操做系統和多線程環境的基本特徵。
1、上下文切換體如今單核上,cpu的時間片分給不一樣的線程,當時間片用完後就會切換到另一個線程上,這也是線程併發執行的一個體現。 2、當前任務執行完時間片後會保存當前任務的一個狀態(執行到哪了),以便下次回來時直接從這個狀態執行,從任務保存到(另外任務)再加載的過程就是一次上下文切換
11. 你如何確保main()方法所在的線程是Java程序最後結束的線程?
咱們可使用Thread類的joint()方法來確保全部程序建立的線程在main()方法退出前結束。
當線程間是能夠共享資源時,線程間通訊是協調它們的重要的手段。Object類中wait()notify()notifyAll()方法能夠用於線程間通訊關於資源的鎖的狀態。
1、使用Thread的join方法(在main方法中調用就緒狀態的子線程的join方法),使子線程先執行。 2、使用閉鎖(CountDownLatch)
13.爲何線程通訊的方法wait(), notify()和notifyAll()被定義在Object類裏?
Java的每一個對象中都有一個鎖(monitor,也能夠成爲監視器) 而且wait(),notify()等方法用於等待對象的鎖或者通知其餘線程對象的監視器可用。在Java的線程中並無可供任何對象使用的鎖和同步器。這就是爲何這些方法是Object類的一部分,這樣Java的每個類都有用於線程間通訊的基本方法
一、最先時候是用於線程間通信協做的。當時,java線程中沒有提供能夠任何對象都使用的鎖或同步器。
二、jdk1.5後lock代替了synchronized,condition代替了對象鎖
14. 爲何wait(), notify()和notifyAll()必須在同步方法或者同步塊中被調用?
當一個線程須要調用對象的wait()方法的時候,這個線程必須擁有該對象的鎖,接着它就會釋放這個對象鎖並進入等待狀態直到其餘線程調用這個對象上的notify()方法。一樣的,當一個線程須要調用對象的notify()方法時,它會釋放這個對象的鎖,以便其餘在等待的線程就能夠獲得這個對象鎖。因爲全部的這些方法都須要線程持有對象的鎖,這樣就只能經過同步來實現,因此他們只能在同步方法或者同步塊中被調用。
1、notify()、wait()是喚醒其餘線程來搶佔當前對象上的鎖,wait是釋放當前對象上的鎖,他們操做的重點就是對象鎖且前提是持有這把鎖。 2、同步方法和同步塊中的操做正好符合了這個前提。
15. 爲何Thread類的sleep()和yield()方法是靜態的?
Thread類的sleep()和yield()方法將在當前正在執行的線程上運行。因此在其餘處於等待狀態的線程上調用這些方法是沒有意義的。這就是爲何這些方法是靜態的。它們能夠在當前正在執行的線程中工做,並避免程序員錯誤的認爲能夠在其餘非運行線程調用這些方法。
是對正在運行的線程調用纔有意義。
16.如何確保線程安全?
在Java中能夠有不少方法來保證線程安全——同步,使用原子類(atomic concurrent classes),實現併發鎖,使用volatile關鍵字,使用不變類和線程安全類。
1、線程不安全的前提是內容被共享,內容的操做不是原子行。 2、相對的,一種是保持內容不共享或線程封閉或不可變,如沒有類屬性的定義,threadlocal,final修飾;
保持屬性操做的原子性,如內置鎖,顯示鎖,原子類
17. volatile關鍵字在Java中有什麼做用?
當咱們使用volatile關鍵字去修飾變量的時候,因此線程都會直接讀取該變量而且不緩存它。這就確保了線程讀取到的變量是同內存中是一致的。
18. 同步方法和同步塊,哪一個是更好的選擇?
同步塊是更好的選擇,由於它不會鎖住整個對象(固然你也可讓它鎖住整個對象)。同步方法會鎖住整個對象,哪怕這個類中有多個不相關聯的同步塊,這一般會致使他們中止執行並須要等待得到這個對象上的鎖。
1、同步方法是對當前整個對象加鎖,調用該對象不一樣的方法都要先獲取該對象的鎖,效率低。 2、同步塊既能夠對當前整個對象加鎖(加在this關鍵字上),也能夠不鎖住整個對象(添加在私有的屬性上)
19.如何建立守護線程?
使用Thread類的setDaemon(true)方法能夠將線程設置爲守護線程,須要注意的是,須要在調用start()方法前調用這個方法,不然會拋出IllegalThreadStateException異常。
20. 什麼是ThreadLocal?
ThreadLocal用於建立線程的本地變量,咱們知道一個對象的全部線程會共享它的全局變量,因此這些變量不是線程安全的,咱們可使用同步技術。可是當咱們不想使用同步的時候,咱們能夠選擇ThreadLocal變量。
每一個線程都會擁有他們本身的Thread變量,它們可使用get()set()方法去獲取他們的默認值或者在線程內部改變他們的值。ThreadLocal實例一般是但願它們同線程狀態關聯起來是private static屬性。
1、threadlocal是將變量封裝到線程中,是一種以空間換時間的作法。 2、它的作法就是將當前threadlocal對象做爲key,value是一個map維護到Trhead對象中。 3、正由於如上,因此threadlocal聲明時使用private static 修飾
21. 什麼是Thread Group?爲何建議使用它?
ThreadGroup是一個類,它的目的是提供關於線程組的信息。
ThreadGroup API比較薄弱,它並無比Thread提供了更多的功能。它有兩個主要的功能:一是獲取線程組中處於活躍狀態線程的列表;二是設置爲線程設置未捕獲異常處理器(ncaught exception handler)。但在Java 1.5中Thread類也添加了setUncaughtExceptionHandler(UncaughtExceptionHandler eh)方法,因此ThreadGroup是已通過時的,不建議繼續使用。
t1.setUncaughtExceptionHandler(newUncaughtExceptionHandler(){
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("exception occured:"+e.getMessage());
}
});
22. 什麼是Java線程轉儲(Thread Dump),如何獲得它?
線程轉儲是一個JVM活動線程的列表,它對於分析系統瓶頸和死鎖很是有用。有不少方法能夠獲取線程轉儲——使用Profiler,Kill -3命令,jstack工具等等。我更喜歡jstack工具,由於它容易使用而且是JDK自帶的。因爲它是一個基於終端的工具,因此咱們能夠編寫一些腳本去定時的產生線程轉儲以待分析。
1、一個jvm活動線程列表,多用於分析系統瓶頸和死鎖。 2、經常使用的轉出工具備Profiler,kill -3,jstack
23. 什麼是死鎖(Deadlock)?如何分析和避免死鎖?
死鎖是指兩個以上的線程永遠阻塞的狀況,這種狀況產生至少須要兩個以上的線程和兩個以上的資源。
分析死鎖,咱們須要查看Java應用程序的線程轉儲。咱們須要找出那些狀態爲BLOCKED的線程和他們等待的資源。每一個資源都有一個惟一的id,用這個id咱們能夠找出哪些線程已經擁有了它的對象鎖。
避免嵌套鎖,只在須要的地方使用鎖和避免無限期等待是避免死鎖的一般辦法,去學習如何分析死鎖。
24. 什麼是Java Timer類?如何建立一個有特定時間間隔的任務?
java.util.Timer是一個工具類,能夠用於安排一個線程在將來的某個特定時間執行。Timer類能夠用安排一次性任務或者週期任務。
java.util.TimerTask是一個實現了Runnable接口的抽象類,咱們須要去繼承這個類來建立咱們本身的定時任務並使用Timer去安排它的執行。
1、timer是一個工具類,用於週期性執行某個執行任務。 2、具體經過new Time().schedule(TimerTask),具體任務要實現TimerTask抽象類。 三、不知是否有任務延後問題(前一個線程執行時間長,致使後任務延後執行)?
25. 什麼是線程池?如何建立一個Java線程池?
一個線程池管理了一組工做線程,同時它還包括了一個用於放置等待執行的任務的隊列。
java.util.concurrent.Executors提供了一個java.util.concurrent.Executor接口的實現用於建立線程池。線程池例子展示瞭如何建立和使用線程池,或者ScheduledThreadPoolExecutor例子,瞭解如何建立一個週期任務
1、線程池用於管理一組工做線程,同時還包括用於放置待執行的任務的隊列。 2、juc中的Exceutors是通用接口,經過Executors來實現具體的線程池的建立。 3、目前包括,指定線程數,單個線程,自動擴充,延遲,forkjoin拆分。
ps:
由於newFixedTreadPool的阻塞隊列無限制和newCachedThreadPool線程個數沒限制,因此不建議使用。
原文參見:https://blog.csdn.net/bucai1126/article/details/79438248