標籤:不同 威脅 無法 擴充 阻塞 自己 end count 定義
Thread.start()方法(native)啟動線程,使之進入就緒狀態,當cpu分配時間該線程時,由JVM調度執行run()方法。
當你調用start()方法時你將建立新的線程,並且執行在run()方法裡的代碼。但是如果你直接調用run()方法,它不會建立新的線程也不會執行調用線程的代碼。
2、Java中Runnable和Callable有什麼不同?
Runnable和Callable都代表那些要在不同的線程中執行的任務。Runnable從JDK1.0開始就有了,Callable是在JDK1.5增加的。它們的主要區別是Callable的 call() 方法可以傳回值和拋出異常,而Runnable的run()方法沒有這些功能。Callable可以返回裝載有計算結果的Future對象。
3、Java中如何停止一個線程?
Java提供了很豐富的API但沒有為停止線程提供API。JDK 1.0本來有一些像stop(), suspend() 和 resume()的控制方法但是由於潛在的死結威脅因此在後續的JDK版本中他們被棄用了,之後Java API的設計者就沒有提供一個相容且安全執行緒的方法來停止一個線程。當run() 或者 call() 方法執行完的時候線程會自動結束,如果要手動結束一個線程,你可以用volatile 布爾變數來退出run()方法的迴圈或者是取消任務來中斷線程
4、一個線程運行時發生異常會怎樣?
如果異常沒有被捕獲該線程將會停止執行。Thread.UncaughtExceptionHandler是用於處理未捕獲異常造成線程突然中斷情況的一個內嵌介面。當一個未捕獲異常將造成線程中斷的時候JVM會使用Thread.getUncaughtExceptionHandler()來查詢線程的UncaughtExceptionHandler並將線程和異常作為參數傳遞給handler的uncaughtException()方法進行處理。
5、為什麼wait, notify 和 notifyAll這些方法不在thread類裡面?
這是個設計相關的問題,它考察的是面試者對現有系統和一些普遍存在但看起來不合理的事物的看法。回答這些問題的時候,你要說明為什麼把這些方法放在Object類裡是有意義的,還有不把它放在Thread類裡的原因。一個很明顯的原因是JAVA提供的鎖是對象級的而不是線程級的,每個對象都有鎖,通過線程獲得。如果線程需要等待某些鎖那麼調用對象中的wait()方法就有意義了。如果wait()方法定義在Thread類中,線程正在等待的是哪個鎖就不明顯了。簡單的說,由於wait,notify和notifyAll都是鎖層級的操作,所以把他們定義在Object類中因為鎖屬於對象。
6、什麼是FutureTask?
在Java並發程式中FutureTask表示一個可以取消的非同步運算。它有啟動和取消運算、查詢運算是否完成和取回運算結果等方法。只有當運算完成的時候結果才能取回,如果運算尚未完成get方法將會阻塞。一個FutureTask對象可以對調用了Callable和Runnable的對象進行封裝,由於FutureTask也是調用了Runnable介面所以它可以提交給Executor來執行。
7、Java中interrupted 和 isInterruptedd方法的區別?
interrupted() 和 isInterrupted()的主要區別是前者會將中斷狀態清除而後者不會。Java多線程的中斷機制是用內部標識來實現的,調用Thread.interrupt()來中斷一個線程就會設定中斷標識為true。當中斷線程調用靜態方式Thread.interrupted()來檢查中斷狀態時,中斷狀態會被清零。而非靜態方法isInterrupted()用來查詢其它線程的中斷狀態且不會改變中斷狀態標識。簡單的說就是任何拋出InterruptedException異常的方法都會將中斷狀態清零。無論如何,一個線程的中斷狀態有有可能被其它線程調用中斷來改變。
8、Java中的同步集合與並發集合有什麼區別?
同步集合與並發集合都為多線程和並發提供了合適的安全執行緒的集合,不過並發集合的可擴充性更高。在Java1.5之前程式員們只有同步集合來用且在多線程並發的時候會導致爭用,阻礙了系統的擴充性。Java5介紹了並發集合像ConcurrentHashMap,不僅提供安全執行緒還用鎖分離和內部分區等現代技術提高了可擴充性。
9、Java中堆和棧有什麼不同?
為什麼把這個問題歸類在多線程和並發麵試題裡?因為棧是一塊和線程緊密相關的記憶體地區。每個線程都有自己的棧記憶體,用於儲存本地變數,方法參數和棧調用,一個線程中儲存的變數對其它線程是不可見的。而堆是所有線程共用的一片公用記憶體地區。對象都在堆裡建立,為了提升效率線程會從堆中弄一個緩衝到自己的棧,如果多個線程使用該變數就可能引發問題,這時volatile 變數就可以發揮作用了,它要求線程從主存中讀取變數的值。
10、如何寫代碼來解決生產者消費者問題?
比較低級的辦法是用wait和notify來解決這個問題,比較贊的辦法是用Semaphore 或者 BlockingQueue來實現生產者消費者模型。
11、 如何避免死結?
死結是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。這是一個嚴重的問題,因為死結會讓你的程式掛起無法完成任務,死結的發生必須滿足以下四個條件:
- 互斥條件:一個資源每次只能被一個進程使用。
- 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
- 不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
- 迴圈等待條件:若干進程之間形成一種頭尾相接的迴圈等待資源關係。
避免死結最簡單的方法就是阻止迴圈等待條件,將系統中所有的資源設定標誌位、排序,規定所有的進程申請資源必須以一定的順序(升序或降序)做操作來避免死結
12、Java中活鎖和死結有什麼區別?
活鎖和死結類似,不同之處在於處於活鎖的線程或進程的狀態是不斷改變的,活鎖可以認為是一種特殊的饑餓。一個現實的活鎖例子是兩個人在狹小的走廊碰到,兩個人都試著避讓對方好讓彼此通過,但是因為避讓的方向都一樣導致最後誰都不能通過走廊。簡單的說就是,活鎖和死結的主要區別是前者進程的狀態可以改變但是卻不能繼續執行。
13、怎麼檢測一個線程是否擁有鎖?
在java.lang.Thread中有一個方法叫holdsLock(),它返回true如果若且唯若當前線程擁有某個具體對象的鎖.
14、如果你提交任務時,線程池隊列已滿。會時發會生什嗎?
這個問題問得很狡猾,許多程式員會認為該任務會阻塞直到線程池隊列有空位。事實上如果一個任務不能被調度執行那麼ThreadPoolExecutor’s submit()方法將會拋出一個RejectedExecutionException異常。
15、Java線程池中submit() 和 execute()方法有什麼區別?
兩個方法都可以向線程池提交任務,execute()方法的傳回型別是void,它定義在Executor介面中, 而submit()方法可以返回持有計算結果的Future對象,它定義在ExecutorService介面中,它擴充了Executor介面,其它線程池類像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有這些方法.
16、Java中的ReadWriteLock是什嗎?
一般而言,讀寫鎖是用來提升並發程式效能的鎖分離技術的成果。Java中的ReadWriteLock是Java 5 中新增的一個介面,一個ReadWriteLock維護一對關聯的鎖,一個用於唯讀操作一個用於寫。在沒有寫線程的情況下一個讀鎖可能會同時被多個讀線程持有。寫鎖是獨佔的,你可以使用JDK中的ReentrantReadWriteLock來實現這個規則,它最多支援65535個寫鎖和65535個讀鎖。
17、volatile 變數和 atomic 變數有什麼不同?
這是個有趣的問題。首先,volatile 變數和 atomic 變數看起來很像,但功能卻不一樣。Volatile變數可以確保先行關係,即寫操作會發生在後續的讀操作之前, 但它並不能保證原子性。例如用volatile修飾count變數那麼 count++ 操作就不是原子性的。而AtomicInteger類提供的atomic方法可以讓這種操作具有原子性如getAndIncrement()方法會原子性的進行增量操作把當前值加一,其它資料類型和引用變數也可以進行相似操作。
JAVA多線程17個問題