更新時間:2019-04-12 09:00:58 來源:動力節點 瀏覽3086次
今天來總結以下Java并發編程的幾道常見面試題,希望可以幫助到大家;
1、在Java中守護線程和本地線程區別?
Java中的線程分為兩種:守護線程(Daemon)和用戶線程(User)。
任何線程都可以設置為守護線程和用戶線程,通過方法Thread.setDaemon(boolon);true則把該線程設置為守護線程,反之則為用戶線程。Thread.setDaemon()必須在Thread.start()之前調用,否則運行時會拋出異常。
兩者的區別:
唯一的區別是判斷虛擬機(JVM)何時離開,Daemon是為其他線程提供服務,如果全部的UserThread已經撤離,Daemon沒有可服務的線程,JVM撤離。也可以理解為守護線程是JVM自動創建的線程(但不一定),用戶線程是程序創建的線程;比如JVM的垃圾回收線程是一個守護線程,當所有線程已經撤離,不再產生垃圾,守護線程自然就沒事可干了,當垃圾回收線程是Java虛擬機上僅剩的線程時,Java虛擬機會自動離開。
擴展:ThreadDump打印出來的線程信息,含有daemon字樣的線程即為守護進程,可能會有:服務守護進程、編譯守護進程、windows下的監聽Ctrl+break的守護進程、Finalizer守護進程、引用處理守護進程、GC守護進程。
2、什么是多線程中的上下文切換?
多線程會共同使用一組計算機上的CPU,而線程數大于給程序分配的CPU數量時,為了讓各個線程都有執行的機會,就需要輪轉使用CPU。不同的線程切換使用CPU發生的切換數據等就是上下文切換。
3、死鎖與活鎖的區別,死鎖與饑餓的區別?
死鎖:是指兩個或兩個以上的進程(或線程)在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。
產生死鎖的必要條件:
互斥條件:所謂互斥就是進程在某一時間內獨占資源。
請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
不剝奪條件:進程已獲得資源,在末使用完之前,不能強行剝奪。
循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。
活鎖:任務或者執行者沒有被阻塞,由于某些條件沒有滿足,導致一直重復嘗試,失敗,嘗試,失敗。
活鎖和死鎖的區別在于,處于活鎖的實體是在不斷的改變狀態,所謂的“活”,而處于死鎖的實體表現為等待;活鎖有可能自行解開,死鎖則不能。
饑餓:一個或者多個線程因為種種原因無法獲得所需要的資源,導致一直無法執行的狀態。
Java中導致饑餓的原因:
高優先級線程吞噬所有的低優先級線程的CPU時間。
線程被永久堵塞在一個等待進入同步塊的狀態,因為其他線程總是能在它之前持續地對該同步塊進行訪問。
線程在等待一個本身也處于永久等待完成的對象(比如調用這個對象的wait方法),因為其他線程總是被持續地獲得喚醒。
4、Java中用到的線程調度算法是什么?
采用時間片輪轉的方式。可以設置線程的優先級,會映射到下層的系統上面的優先級上,如非特別需要,盡量不要用,防止線程饑餓。
5、什么是線程組,為什么在Java中不推薦使用?
ThreadGroup類,可以把線程歸屬到某一個線程組中,線程組中可以有線程對象,也可以有線程組,組中還可以有線程,這樣的組織結構有點類似于樹的形式。
為什么不推薦使用?因為使用有很多的安全隱患吧,沒有具體追究,如果需要使用,推薦使用線程池。
6、為什么使用Executor框架?
每次執行任務創建線程newThread()比較消耗性能,創建一個線程是比較耗時、耗資源的。
調用newThread()創建的線程缺乏管理,被稱為野線程,而且可以無限制的創建,線程之間的相互競爭會導致過多占用系統資源而導致系統癱瘓,還有線程之間的頻繁交替也會消耗很多系統資源。
接使用newThread()啟動的線程不利于擴展,比如定時執行、定期執行、定時定期執行、線程中斷等都不便實現。
7、JavaConcurrencyAPI中的Lock接口(Lockinterface)是什么?對比同步它有什么優勢?
Lock接口比同步方法和同步塊提供了更具擴展性的鎖操作。
他們允許更靈活的結構,可以具有完全不同的性質,并且可以支持多個相關類的條件對象。
它的優勢有:
可以使鎖更公平
可以使線程在等待鎖的時候響應中斷
可以讓線程嘗試獲取鎖,并在無法獲取鎖的時候立即返回或者等待一段時間
可以在不同的范圍,以不同的順序獲取和釋放鎖
整體上來說Lock是synchronized的擴展版,Lock提供了無條件的、可輪詢的(tryLock方法)、定時的(tryLock帶參方法)、可中斷的(lockInterruptibly)、可多條件隊列的(newCondition方法)鎖操作。另外Lock的實現類基本都支持非公平鎖(默認)和公平鎖,synchronized只支持非公平鎖,當然,在大部分情況下,非公平鎖是高效的選擇。
8、什么是Callable和Future?
Callable接口類似于Runnable,從名字就可以看出來了,但是Runnable不會返回結果,并且無法拋出返回結果的異常,而Callable功能更強大一些,被線程執行后,可以返回值,這個返回值可以被Future拿到,也就是說,Future可以拿到異步執行任務的返回值。
可以認為是帶有回調的Runnable。
Future接口表示異步任務,是還沒有完成的任務給出的未來結果。所以說Callable用于產生結果,Future用于獲取結果。
9、什么是FutureTask?使用ExecutorService啟動任務。
在Java并發程序中FutureTask表示一個可以取消的異步運算。它有啟動和取消運算、查詢運算是否完成和取回運算結果等方法。只有當運算完成的時候結果才能取回,如果運算尚未完成get方法將會阻塞。一個FutureTask對象可以對調用了Callable和Runnable的對象進行包裝,由于FutureTask也是調用了Runnable接口所以它可以提交給Executor來執行。
10、什么是并發容器的實現?
何為同步容器:可以簡單地理解為通過synchronized來實現同步的容器,如果有多個線程調用同步容器的方法,它們將會串行執行。比如Vector,Hashtable,以及Collections.synchronizedSet,synchronizedList等方法返回的容器。
可以通過查看Vector,Hashtable等這些同步容器的實現代碼,可以看到這些容器實現線程安全的方式就是將它們的狀態封裝起來,并在需要同步的方法上加上關鍵字synchronized。
并發容器使用了與同步容器完全不同的加鎖策略來提供更高的并發性和伸縮性,例如在ConcurrentHashMap中采用了一種粒度更細的加鎖機制,可以稱為分段鎖,在這種鎖機制下,允許任意數量的讀線程并發地訪問map,并且執行讀操作的線程和寫操作的線程也可以并發的訪問map,同時允許一定數量的寫操作線程并發地修改map,所以它可以在并發環境下實現更高的吞吐量。
11、如何停止一個正在運行的線程?
使用共享變量的方式
在這種方式中,之所以引入共享變量,是因為該變量可以被多個執行相同任務的線程用來作為是否中斷的信號,通知中斷線程的執行。
使用interrupt方法終止線程
如果一個線程由于等待某些事件的發生而被阻塞,又該怎樣停止該線程呢?這種情況經常會發生,比如當一個線程由于需要等候鍵盤輸入而被阻塞,或者調用Thread.join()方法,或者Thread.sleep()方法,在網絡中調用ServerSocket.accept()方法,或者調用了DatagramSocket.receive()方法時,都有可能導致線程阻塞,使線程處于處于不可運行狀態時,即使主程序中將該線程的共享變量設置為true,但該線程此時根本無法檢查循環標志,當然也就無法立即中斷。這里我們給出的建議是,不要使用stop()方法,而是使用Thread提供的interrupt()方法,因為該方法雖然不會中斷一個正在運行的線程,但是它可以使一個被阻塞的線程拋出一個中斷異常,從而使線程提前結束阻塞狀態,退出堵塞代碼。
12、什么是Daemon線程?它有什么意義?
所謂后臺(daemon)線程,是指在程序運行的時候在后臺提供一種通用服務的線程,并且這個線程并不屬于程序中不可或缺的部分。因此,當所有的非后臺線程結束時,程序也就終止了,同時會殺死進程中的所有后臺線程。
反過來說,只要有任何非后臺線程還在運行,程序就不會終止。必須在線程啟動之前調用setDaemon()方法,才能把它設置為后臺線程。注意:后臺進程在不執行finally子句的情況下就會終止其run()方法。
比如:JVM的垃圾回收線程就是Daemon線程,Finalizer也是守護線程。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習