Java 面試題 -- 多線程

1.並行和併發有什麼區別?

並行是指兩個或者多個事件在同一時刻發生;而併發是指兩個或多個事件在同一時間間隔發生。
並行是在不同實體上的多個事件,併發是在同一實體上的多個事件。
在一臺處理器上“同時”處理多個任務,在多臺處理器上同時處理多個任務。如hadoop分佈式集羣。
所以併發編程的目標是充分的利用處理器的每一個核,以達到最高的處理性能。

2.線程和進程的區別?

簡而言之,進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程。進程在執行過程中擁有獨立的內存單元,而多個線程共享內存資源,減少切換次數,從而效率更高。線程是進程的一個實體,是cpu調度和分派的基本單位,是比程序更小的能獨立運行的基本單位。同一進程中的多個線程之間可以併發執行。

3.守護線程是什麼?

守護線程(即daemon thread),是個服務線程,準確地來說就是服務其他的線程。

4.創建線程有哪幾種方式?

①. 繼承Thread類創建線程類
定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就代表了線程要完成的任務。因此把run()方法稱爲執行體。
創建Thread子類的實例,即創建了線程對象。
調用線程對象的start()方法來啓動該線程。

②. 通過Runnable接口創建線程類
定義runnable接口的實現類,並重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執行體。
創建 Runnable實現類的實例,並依此實例作爲Thread的target來創建Thread對象,該Thread對象纔是真正的線程對象。
調用線程對象的start()方法來啓動該線程。

③. 通過Callable和Future創建線程
創建Callable接口的實現類,並實現call()方法,該call()方法將作爲線程執行體,並且有返回值。
創建Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。
使用FutureTask對象作爲Thread對象的target創建並啓動新線程。
調用FutureTask對象的get()方法來獲得子線程執行結束後的返回值。

5.說一下 runnable 和 callable 有什麼區別?

Runnable接口中的run()方法的返回值是void,它做的事情只是純粹地去執行run()方法中的代碼而已;
Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合可以用來獲取異步執行的結果。

6.線程有哪些狀態?

線程通常都有五種狀態,創建、就緒、運行、阻塞和死亡。

創建狀態:在生成線程對象,並沒有調用該對象的start方法,這是線程處於創建狀態。
就緒狀態:當調用了線程對象的start方法之後,該線程就進入了就緒狀態,但是此時線程調度程序還沒有把該線程設置爲當前線                    程,此時處於就緒狀態。在線程運行之後,從等待或者睡眠中回來之後,也會處於就緒狀態。
運行狀態:線程調度程序將處於就緒狀態的線程設置爲當前線程,此時線程就進入了運行狀態,開始運行run函數當中的代碼。
阻塞狀態:線程正在運行的時候,被暫停,通常是爲了等待某個時間的發生(比如說某項資源就緒)之後再繼續運行。                                        sleep,suspend,wait等方法都可以導致線程阻塞。
死亡狀態:如果一個線程的run方法執行結束或者調用stop方法後,該線程就會死亡。對於已經死亡的線程,無法再使用start方法                    令其進入就緒。

7.sleep() 和 wait() 有什麼區別?

sleep():方法是線程類(Thread)的靜態方法,讓調用線程進入睡眠狀態,讓出執行機會給其他線程,等到休眠時間結束後,線程進入就緒狀態和其他線程一起競爭cpu的執行時間。因爲sleep() 是static靜態的方法,他不能改變對象的機鎖,當一個synchronized塊中調用了sleep() 方法,線程雖然進入休眠,但是對象的機鎖沒有被釋放,其他線程依然無法訪問這個對象。

wait():wait()是Object類的方法,當一個線程執行到wait方法時,它就進入到一個和該對象相關的等待池,同時釋放對象的機鎖,使得其他線程能夠訪問,可以通過notify,notifyAll方法來喚醒等待的線程。

8.notify()和 notifyAll()有什麼區別?

如果線程調用了對象的 wait()方法,那麼線程便會處於該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
當有線程調用了對象的 notifyAll()方法(喚醒所有 wait 線程)或 notify()方法(只隨機喚醒一個 wait 線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。也就是說,調用了notify後只要一個線程會由等待池進入鎖池,而notifyAll會將該對象等待池內的所有線程移動到鎖池中,等待鎖競爭。
優先級高的線程競爭到對象鎖的概率大,假若某線程沒有競爭到該對象鎖,它還會留在鎖池中,唯有線程再次調用 wait()方法,它纔會重新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了 synchronized 代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。

9.在 java 程序中怎麼保證多線程的運行安全?

線程安全在三個方面體現:
原子性:提供互斥訪問,同一時刻只能有一個線程對數據進行操作,(atomic,synchronized);
可見性:一個線程對主內存的修改可以及時地被其他線程看到,(synchronized,volatile);
有序性:一個線程觀察其他線程中的指令執行順序,由於指令重排序,該觀察結果一般雜亂無序,(happens-before原則)。

10.什麼是死鎖?

死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。是操作系統層面的一個錯誤,是進程死鎖的簡稱,最早在 1965 年由 Dijkstra 在研究銀行家算法時提出的,它是計算機操作系統乃至整個併發程序設計領域最難處理的問題之一。

11.說一下 synchronized 底層實現原理?

synchronized可以保證方法或者代碼塊在運行時,同一時刻只有一個方法可以進入到臨界區,同時它還可以保證共享變量的內存可見性。

Java中每一個對象都可以作爲鎖,這是synchronized實現同步的基礎:
普通同步方法,鎖是當前實例對象
靜態同步方法,鎖是當前類的class對象
同步方法塊,鎖是括號裏面的對象

 

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