- 相關推薦
常見的Java多線程面試問題及解答
問題:進程和線程的區別
解答:一個進程對應一個程序的執行,而一個線程則是進程執行過程中的一個單獨的執行序列,一個進程可以包含多個線程。線程有時候也被稱為輕量級進程.
一個Java虛擬機的實例運行在一個單獨的進程中,不同的線程共享Java虛擬機進程所屬的堆內存。這也是為什么不同的線程可以訪問同一個對象。線 程彼此共享堆內存并保有他們自己獨自的?臻g。這也是為什么當一個線程調用一個方法時,他的局部變量可以保證線程安全。但堆內存并不是線程安全的,必須通 過顯示的聲明同步來確保線程安全。
問題:列舉幾種不同的創建線程的方法.
解答:可以通過如下幾種方式:
繼承Thread 類
實現Runnable 接口
使用Executor framework (這會創建一個線程池)
123456789101112131415class Counter extends Thread {//method where the thread execution will start public void run(){//logic to execute in a thread }//let’s see how to start the threadspublic static void main(String[] args){Thread t1 = new Counter();Thread t2 = new Counter();t1.start(); //start the first thread. This calls the run() method.t2.start(); //this starts the 2nd thread. This calls the run() method. }}123456789101112131415class Counter extends Base implements Runnable{//method where the thread execution will start public void run(){//logic to execute in a thread }//let us see how to start the threadspublic static void main(String[] args){Thread t1 = new Thread(new Counter());Thread t2 = new Thread(new Counter());t1.start(); //start the first thread. This calls the run() method.t2.start(); //this starts the 2nd thread. This calls the run() method. }}
通過線程池來創建更有效率。
問題:推薦通過哪種方式創建線程,為什么?
解答:最好使用Runnable接口,這樣你的類就不必繼承Thread類,不然當你需要多重繼承的時候,你將 一籌莫展(我們都知道Java中的類只能繼承自一個類,但可以同時實現多個接口)。在上面的例子中,因為我們要繼承Base類,所以實現Runnable 接口成了顯而易見的選擇。同時你也要注意到在不同的例子中,線程是如何啟動的。按照面向對象的方法論,你應該只在希望改變父類的行為的時候才去繼承他。通 過實現Runnable接口來代替繼承Thread類可以告訴使用者Counter是Base類型的一個對象,并會作為線程執行。
問題:簡要的說明一下高級線程狀態.
解答:下圖說明了線程的各種狀態.
可執行(Runnable):當調用start()方法后,一個線程變為可執行狀態,但是并不意味著他會立刻開始真正地執行。而是被放入線程池,
由線程調度器根據線程優先級決定何時掛起執行。
12MyThread aThread = new MyThread();aThread.start(); //becomes runnable
執行中(Running):處理器已經在執行線程的代碼。他會一直運行直到被阻斷,或者通過靜態方法Thread.yield()自行放棄執行的機會,考慮到場景切換所帶來的開銷,yield()方法不應該被經常調用。
等待中(Waiting):線程由于等待I/O等外部進程的處理結果而處于被阻斷的狀態,調用currObject.wait( )方法會使得當前線程進入等待狀態,直到其它線程調用currObject.notify() 或者currObject.notifyAll() 。
睡眠中(Sleeping):重載方法Thread.sleep(milliseconds),Thread.sleep(milliseconds, nanoseconds)可以迫使Java線程進入睡眠狀態(掛起)。
由于I/O阻塞(Blocked on I/O):當I/O條件發生變化時(例如讀取了幾個字節的數據)會遷移到可執行狀態。
由于同步阻塞中(Blocked on synchronization): 當獲取鎖之后會進入執行中狀態。
Thread.State 枚舉類型包含了Java虛擬機支持的全部的線程狀態類型,下面幾點Java的線程宗旨確保了這些線程狀態成為可能。
對象可以被任何線程共享和修改。
線程調度器的搶占性特性,使得線程可以隨時在/不在多核處理之間切換處理器內核,這意味著方法可以在執行的過程中切換狀態。否則方法中的死循環將永遠阻塞CPU,并且使得不同線程的其他方法始終得不到執行。
為了防止線程安全問題,那些脆弱的方法或者代碼塊可以被鎖定。這使得線程可以處于被鎖定或者加鎖請求處理中兩種狀態。
線程在處理I/O資源(如Sockets,文件句柄,數據庫連接等)時會進入等待狀態,
處于I/O讀寫中的線程不能被切換,因此他們或者以成功/失敗的結果正常完成處理,或者其它線程關閉了相應的資源,迫使他進入死亡或者完成的狀態。這也是為什么一個合理的超時時間可以避免線程由于I/O處理而被永遠阻塞,從而導致嚴重的性能問題。
線程可以進入睡眠狀態,以使得其他處于等待狀態的線程有機會執行。
問題:yield和sleeping有何區別,sleep()和wait()有何區別?
解答:當一個任務調用了yield()方法,它將從執行中狀態轉變為可執行。而當一個任務調用了sleep(),則將從執行中狀態轉變為等待中/睡眠中狀態。
方法wait(1000)使得當前線程睡眠1秒鐘,但調用notify() 或者notifyAll()會隨時喚醒線程。而sleep(1000)則會導致當前線程休眠1秒鐘。
問題:為什么為了線程安全而鎖定一個方法或者一個代碼塊稱為“同步”而不是“鎖定”或者“被鎖定”
解答:當某個方法或者代碼塊被聲明為”synchronized”后,保存數據的內存空間(例如堆內存)將保持被同步狀態。
這意味著:當一個線程獲取鎖并且執行到已被聲明為synchronized的方法或者代碼塊時,該線程首先從主堆內存空間中讀取該鎖定對象的所有變 化,以確保其在開始執行之前擁有最新的信息。在synchronized部分執行完畢,線程準備釋放鎖的時候,所有針對被鎖定對象的修改都將為寫入主堆內 存中。這樣其他線程在請求鎖的時候就可以獲取最新的信息。
問題:線程如何進行的同步處理?你可以列舉出那些同步級別?同步方法和代碼塊如何區別?
解答:在Java語言中,每個對象都有一個鎖,一個線程可以通過關鍵字synchronized來申請獲取某個 對象的鎖,關鍵字synchronized可以被用于方法(粗粒度鎖,對性能影響較大)或代碼塊(細粒度鎖)級別。鎖定方法往往不是一個很好的選擇,取而 代之的我們應該只鎖定那些訪問共享資源的代碼塊,因為每一個對象都有一個鎖,所以可以通過創建虛擬對象來實現代碼塊級別的同步,方法塊級別的鎖比鎖定整個 方法更有效。
Java虛擬機靈活的使用鎖和監視器,一個監視器總體來說就是一個守衛者,他負責確保只有一個線程會在同一時間執行被同步的代碼。每個監視器對應一 個對象的引用,在線程執行代碼塊的第一條指令之前,他必須持有該引用對象的鎖,否則他將無法執行這段代碼。一旦他獲得鎖,該線程就可以進入這段受到保護的 代碼。當線程不論以何種方式退出代碼塊時,他都將釋放關聯對象的鎖。對于靜態方法,需要請求類級別的鎖。
【常見的Java多線程面試問題及解答】相關文章:
應屆面試常見問題及解答10-24
護士面試常見問題及解答07-08
外企面試常見問題及解答07-16
面試中最常見的問題及解答08-07
java招聘面試常見問題10-02
java面試最常見問題08-30
面試常見問題解答03-19
英語面試常見問題及解答技巧08-06
常見面試問題與解答思路08-30
英語面試常見問題解答10-23