1、線程和進程
進程是資源分配單位,線程是 CPU 調度單位
線程:進程中執行的每個任務就是線程,是CPU調度和分派的基本單元。
進程:進程是具有一定獨立功能的程序,它是系統進程資源分配和調度的一個獨立單元。
區別:
(1)一個線程只屬於一個進程,一個進程包含一個或者多個線程。
(2)進程擁有獨立的內存單元,而多個線程共享內存。
(3)進程的創建調用fork或者vfork,而線程的創建調用pthead_create,進程結束后它擁有的所有線程都將銷毀,而線程的結束不會影響同個進程中的其他線程的結束。
(4)線程是輕量級的進程,它的創建和銷毀所需要的時間比進程小很多,所有操作系統中的執行功能都是創建線程去完成的。
(5)線程中執行時一般都要進行同步和互斥,因為他們共享同一進程的資源。
2、死鎖?死鎖產生的原因?死鎖的必要條件?怎么處理死鎖?
死鎖:死鎖是指兩個或者兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象。
死鎖原因:系統資源不足、相互競爭資源。請求資源順序不當
死鎖的必要條件:
1.互斥條件:一個資源每次只能被一個進程使用。
2.請求和保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
3.不可剝奪條件:進程已獲得的資源,在未使用完之前,不能強行剝奪,只能在進程使用完時由自己釋放。
4.循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。
避免死鎖的方法:
因為互斥是不可改變的,所以只能破壞其他三個條件中的一個來解除死鎖,方法:剝奪資源、殺死其中一個線程。
避免死鎖最簡單的方法就是阻止循環等待條件,將系統中所有的資源設置標志位、排序,規定所有的進程申請資源必須以一定的順序做操作來避免死鎖。
3、如何在Java中實現線程?
(1)繼承Thread類
(2)實現Runnable接口
(3)實現Callable接口通過FutureTask包裝器來創建Thread線程
(4)使用ExecutorService、Callable、Future實現有返回結果的多線程
4、用Runnable還是Thread?
Java不支持類的多重繼承,但允許你調用多個接口(當然是調用Runnable接口更好)
5、Thread類中start()和run()方法有什么區別?
(1)start()方法被用來啟動新創建的線程,而start()內部調用了run()方法。
(2)直接調用run()方法的時候,只會在原來的線程中調用,沒有新的線程啟動。
6、Java中Runnable和Callable有什么不同?
主要的區別是Callable的call()方法可以返回值和拋出異常,而Runnable的run()方法沒有這些功能
7、Java中的volatile變量是什么?
volatile關鍵字的作用是保證變量在多線程之間的可見性(可見性是說每個線程訪問用volatile修飾變量時,volatile都保證線程能得到當前最新的值)
volatile是java中提供的最輕量的同步機制
8、什么是線程安全?Vector是一個線程安全類嗎?
線程安全:如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,
而且其他的變量的值也和預期的一樣,就是線程安全的。
Vector是用同步方法來實現線程安全的,而和它相似的ArrayList不是線程安全的。
9、Java中如何停止一個線程?
(1)使用退出標志,使線程正常退出,也就是當run方法完成后線程終止。
(2)使用stop方法強行終止,但是不推薦這個方法,因為stop和suspend及resume一樣都是過期作廢的方法。
(3)使用interrupt方法中斷線程。
10、Java中notify和notifyAll有什么區別?
notify()方法不能喚醒某個具體的線程,所以只有一個線程在等待的時候它才有用武之地。而notifyAll()喚醒所有線程並允許他們爭奪鎖確保至少有一個線程能繼續運行。
11、為什么wait、notify和notifyAll這些方法不在thread類里面?
因為wait、notify和notifyAll都是鎖級別的操作,所以把它們定義在Object類中是因為鎖屬於對象。如果定義在Thread類中,線程正在執行的是哪個鎖就不明顯了。
12、什么是TreadLocal變量?
ThreadLocal是Java里一種特殊的變量。每個線程都有一個ThreadLocal就是每個線程都擁有了自己獨立的一個變量,競爭條件被徹底消除了。
13、為什么wait和notify方法要在同步塊中調用?
主要是因為Java API強制要求這樣做,如果你不這樣做,你的代碼會拋出IllegalMonitorStateException異常。還有一個原因是為了避免wait和notify之間產生競態條件。
14、什么是線程池?為什么要使用它?
創建線程要花費昂貴的資源和時間,如果任務來了才創建線程那么響應時間會變長,而且一個進程能創建的線程數有限。為了避免這些問題,在初始化一個多線程應用程序過程中創建一個線程集合,
然后在需要執行新的任務時重用這些線程而不是新建一個線程,它們被稱為線程池,里面的線程叫工作線程。
為什么需要線程池?
(1)線程池改進了一個應用程序的響應時間。
(2)線程池節省了CLR為每個短生存周期任務創建一個完整的線程的開銷並可以在任務完成后回收資源。
(3)線程池根據當前在系統中運行的進程來優化線程時間片。
(4)線程池允許我們開啟多個任務而不用為每個線程設置屬性。
(5)線程池可以用來解決處理一個特定請求最大線程數量限制問題。
15、怎樣檢測一個線程是否擁有鎖?
在java.lang.Thread中有一個方法叫holdslock(),如果當前線程擁有某個具體對象的鎖就返回true。
16、JVM中哪個參數是用來控制線程的堆棧大小的
-Xss參數用來控制線程的堆棧大小。
17、Java中synchronized和ReentrantLock有什么不同?
相同點:
它們都是加鎖方式同步,而且都是阻塞式的同步,也就是說如果一個線程獲得了對象鎖,進入了同步塊,其他訪問該同步塊的線程都必須阻塞在同步塊外面等待。
不同點:
1.對於synchronized來說,它是java的關鍵字,是原生語法層面的互斥,需要jvm實現。
2.ReentrantLock是JDK1.5之后提供的PI層面的互斥鎖,需要lock()和unlock()方法配合try/finally語句塊來完成。
Synchronized通過編譯,會在同步塊的前后分別形成monitorenter和monitorexit這個兩個字節碼指令。在執行monitorenter指令時,首先要嘗試獲取對象鎖。
如果這個對象沒被鎖定,或者當前線程已經擁有了那個對象鎖,把鎖的計算器加1,相應的,在執行monitorexit指令時會將鎖計算器就減1,當計算器為0時,鎖就被釋放了。
如果獲取對象鎖失敗,那當前線程就要阻塞,直到對象鎖被另一個線程釋放為止。
ReentrantLock類提供了一些高級功能,主要有以下3項:
1.等待可中斷,持有鎖的線程長期不釋放的時候,正在等待的線程可以選擇放棄等待,這相當於Synchronized來說可以避免出現死鎖的情況。
2.公平鎖,多個線程等待同一個鎖時,必須按照申請鎖的時間順序獲得鎖,Synchronized鎖非公平鎖,ReentrantLock默認的構造函數是創建的非公平鎖,可以通過參數true設為公平鎖,但公平鎖表現的性能不是很好。
3.鎖綁定多個條件,一個ReentrantLock對象可以同時綁定對個對象。
18、有三個線程T1,T2,T3,怎么確保它們按順序執行?
T3先執行,在T3的run中,調用t2.join,讓t2執行完成后再執行t3,在T2的run中,調用t1.join,讓t1執行完成后再讓T2執行
(join()方法是等待本線程結束后,下一個線程才可以運行)
19、Thread類中的yield方法有什么作用?
yield方法的作用就是說當一個線程使用了這個方法之后,它就會把自己CPU執行的時間讓掉,讓自己或者其它的線程運行。