1. 程式人生 > >java多線程面試中常見知識點

java多線程面試中常見知識點

合數 編譯 強制 live() 多線程 問題 ted 實例 靜態變量

1.進程和線程

  (1)進程是資源分配的最小單位,線程是程序執行的最小單位。

  (2)進程有自己的獨立地址空間,每啟動一個進程,系統就會為它分配地址空間,建立數據表來維護代碼段、堆棧段和數據段,這種操作非常昂貴。而線程是共享進程中的數據的,使用相同的地址空間,因此CPU切換一個線程的花費遠比進程要小很多,同時創建一個線程的開銷也比進程要小很多。

  (3)線程之間的通信更方便,同一進程下的線程共享全局變量、靜態變量等數據,而進程之間的通信需要以通信的方式(IPC)進行。不過如何處理好同步與互斥是編寫多線程程序的難點。

  (4)但是多進程程序更健壯,多線程程序只要有一個線程死掉,整個進程也死掉了,而一個進程死掉並不會對另外一個進程造成影響,因為進程有自己獨立的地址空間。

  (5)線程只由堆棧寄存器,程序計數器,TCB(線程控制塊)組成。

2.Thread中start和run方法的區別

  (1) 調用start()方法會創建一個新的子線程並啟動。

  (2) run()方法只是Thread的一個普通方法的調用。

3.如何給run()方法傳參

(1)構造函數;

(2)成員變量(set和get);

(3)回調函數。

4.如何實現處理線程的返回值?

(1)主線程等待法;

(2)使用Thread類的join()阻塞當前線程以等待子線程處理完畢;(缺點:粒度不夠細);

(3)通過Callable接口實現:通過FutureTask OR 線程池獲取。

5.sleep()和wait()的區別?

(1)sleep是Thread類的方法,wait是Object類中定義的方法(native);

(2)sleep方法可以在任何地方使用;

(3)wait方法只能在syn方法或同步快中使用;

(4)**sleep只會讓出cpu,不會導致鎖行為的改變;

(5)**wait不僅讓出cpu,還會釋放已經占有的同步資源。

6.Java中的鎖池和等待池

  Java平臺中,因為有內置鎖的機制,每個對象都可以承擔鎖的功能。Java虛擬機會為每個對象維護兩個“隊列”(姑且稱之為“隊列”,盡管它不一定符合數據結構上隊列的“先進先出”原則):一個叫Entry Set(入口集),另外一個叫Wait Set(等待集)。對於任意的對象objectX,objectX的Entry Set用於存儲等待獲取objectX這個鎖的所有線程,也就是傳說中的鎖池,objectX的Wait Set用於存儲執行了objectX.wait()/wait(long)的線程,也就是等待池。

7.notify()和notifyAll()的區別?

  notify 僅僅通知一個線程使其進入鎖池,但我們不知道哪個線程會收到通知,然而 notifyAll 會通知所有等待中的線程。

8.yield

  Thread.yield()方法作用是:暫停當前正在執行的線程對象(及放棄當前擁有的cup資源),並執行其他線程。

  yield()做的是讓當前運行線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行機會。因此,使用yield()的目的是讓相同優先級的線程之間能適當的輪轉執行。但是,實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程調度程序再次選中。

9.java中interrupt(),isInterrupted()和interrupted()

(1)當對一個線程,調用 interrupt() 時:

  ① 如果線程處於正常活動狀態,那麽會將該線程的中斷標誌設置為 true。被設置中斷標誌的線程將繼續正常運行,不受影響。

    ② 如果線程處於被阻塞狀態(sleep, wait, join 等狀態),那麽線程將立即退出被阻塞狀態,並拋出一個InterruptedException

  interrupt() 並不能真正的中斷線程,需要被調用的線程自己進行配合才行

  ① 在正常運行任務時,經常檢查本線程的中斷標誌位,如果被設置了中斷標誌就自行停止線程

  ② 在調用阻塞方法時正確處理InterruptedException異常

(2)interrupted()是靜態方法:其作用是測試當前線程是否已經中斷,內部實現是調用的當前線程的isInterrupted(boolean ClearInterrupted),並且會清除當前線程的中斷狀態。(ClearInterrupted參數可以用來控制是否清除中斷標誌)

(3)isInterrupted()是實例方法:isInterrupted()方法用來測試調用該方法的線程的中斷狀態,不會清除當前線程的中斷狀態。

10. CAS(Compare-and-Swap)

  

  通常提到並發編程,在數據的一致性上面,都會考慮用加鎖方式在解決。在這裏,對鎖的解決方案就不多提了,其優勢在於編程簡單,java裏面解決方案很多,synchronized關鍵字或者Lock都能提供原子化操作語意。但其劣勢也非常明顯,加鎖其實並不耗時間,但是其容易造成資源等待,等待是非常耗時的,不合理的設計還容易造成死鎖。更好的辦法就是利用CAS機制實現樂觀鎖(每次不加鎖去完成操作,如果因為沖突失敗就重試,直到成功。)來解決效率問題。

  CAS是由CPU硬件實現,所以執行相當快.CAS有三個操作參數:內存地址,期望值,要修改的新值,當期望值和內存當中的值進行比較不相等的時候,表示內存中的值已經被別線程改動過,這時候失敗返回,當相等的時候,將內存中的值改為新的值,並返回成功。

11. volatile

  volatile是Java提供的一種輕量級的同步機制。Java 語言包含兩種內在的同步機制:同步塊(或方法)和 volatile 變量,相比於synchronized(synchronized通常稱為重量級鎖),volatile更輕量級,因為它不會引起線程上下文的切換和調度。但是volatile 變量的同步性較差(有時它更簡單並且開銷更低),而且其使用也更容易出錯。 

  volatile變量的特性:

  (1)保證可見性,不保證原子性
    a.當寫一個volatile變量時,JMM會把該線程本地內存中的變量強制刷新到主內存中去;
    b.這個寫會操作會導致其他線程中的緩存無效。
  (2)禁止指令重排重排序是指編譯器和處理器為了優化程序性能而對指令序列進行排序的一種手段。重排序需要遵守一定規則:
  a.重排序操作不會對存在數據依賴關系的操作進行重排序。
    比如:a=1;b=a; 這個指令序列,由於第二個操作依賴於第一個操作,所以在編譯時和處理器運行時這兩個操作不會被重排序。
    b.重排序是為了優化性能,但是不管怎麽重排序,單線程下程序的執行結果不能被改變
    比如:a=1;b=2;c=a+b這三個操作,第一步(a=1)和第二步(b=2)由於不存在數據依賴關系, 所以可能會發 生重排序,但是c=a+b這個操作是不會被重排序的,因為需要保證最終的結果一定是c=a+b=3。

12. happens-before

happens-before原則定義如下:

  (1)如果一個操作happens-before(之前發生)另一個操作,那麽第一個操作的執行結果將對第二個操作可見,而且第一個操作的執行順序排在第二個操作之前。

  (2)兩個操作之間存在happens-before關系,並不意味著一定要按照happens-before原則制定的順序來執行。如果重排序之後的執行結果與按照happens-before關系來執行的結果一致,那麽這種重排序並不非法。

happens-before規則如下:
  (1)程序次序規則:一個線程內,按照代碼順序,書寫在前面的操作先行發生於書寫在後面的操作;
  (2)鎖定規則:一個unLock操作先行發生於後面對同一個鎖額lock操作;
  (3)volatile變量規則:對一個變量的寫操作先行發生於後面對這個變量的讀操作;
  (4)傳遞規則:如果操作A先行發生於操作B,而操作B又先行發生於操作C,則可以得出操作A先行發生於操作C;
  (5)線程啟動規則:Thread對象的start()方法先行發生於此線程的每個一個動作;
  (6)線程中斷規則:對線程interrupt()方法的調用先行發生於被中斷線程的代碼檢測到中斷事件的發生;
  (7)線程終結規則:線程中所有的操作都先行發生於線程的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive()的返回值手段檢測到線程已經終止執行;
  (8)對象終結規則:一個對象的初始化完成先行發生於他的finalize()方法的開始;

13. JMM

   JMM(java memory model)java內存模型主要目標是定義程序中的變量,(此處所指的變量是實例字段、靜態字段等,不包含局部變量和函數參數,因為這兩種是線程私有無法共享)在虛擬機中存儲到內存與從內存讀取出來的規則細節,Java 內存模型規定所有變量都存儲在主內存中,每條線程還有自己的工作內存,工作內存保存了該線程使用到的變量到主內存副本拷貝,線程對變量的所有操作(讀取、賦值)都必須在自己的工作內存中進行而不能直接讀寫主內存的變量,不同線程之間無法相互直接訪問對方工作內存中的變量,線程間變量值的傳遞均需要在主內存來完成。

14. ReentrantLock

  未完待續。。。

java多線程面試中常見知識點