一、多線程有什麼用?java
-
1)揮多核CPU 的優點
隨着工業的進步,如今的筆記本、臺式機乃至商用的應用服務器至少也都是雙核的,4 核、8 核甚至 16 核的也都很多見,若是是單線程的程序,那麼在雙核 CPU 上就浪費了 50%, 在 4 核 CPU 上就浪費了 75%。單核 CPU 上所謂的"多線程"那是假的多線程,同一時間處理器只會處理一段邏輯,只不過線程之間切換得比較快,看着像多個線程"同時"運行罷了。多核 CPU 上的多線程纔是真正的多線程,它能讓你的多段邏輯同時工做,多線程,能夠真正發揮出多核CPU 的優點來,達到充分利用CPU 的目的。程序員
-
2)防止阻塞
從程序運行效率的角度來看,單核 CPU 不但不會發揮出多線程的優點,反而會由於在單核CPU 上運行多線程致使線程上下文的切換,而下降程序總體的效率。可是單核 CPU 咱們仍是要應用多線程,就是爲了防止阻塞。試想,若是單核 CPU 使用單線程,那麼只要這個線程阻塞了,比方說遠程讀取某個數據吧,對端遲遲未返回又沒有設置超時時間,那麼你的整個程序在數據返回回來以前就中止運行了。多線程能夠防止這個問題,多條線程同時運行,哪怕一條線程的代碼執行讀取數據阻塞,也不會影響其它任務的執行。面試
- 3)便於建模
這是另一個沒有這麼明顯的優勢了。假設有一個大的任務 A,單線程編程,那麼就要考慮不少,創建整個程序模型比較麻煩。可是若是把這個大的任務 A 分解成幾個小任務,任務B、任務 C、任務 D,分別創建程序模型,並經過多線程分別運行這幾個任務,那就簡單不少了。
二、線程和進程的區別是什麼?算法
- 進程和線程的主要差異在於它們是不一樣的操做系統資源管理方式。
- 進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不一樣執行路徑。
- 線程有本身的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,因此多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。
- 但對於一些要求同時進行而且又要共享某些變量的併發操做,只能用線程,不能用進程。
三、Java 實現線程有哪幾種方式?數據庫
- 一、繼承 Thread 類實現多線程
- 二、實現 Runnable 接口方式實現多線程
- 三、使用 ExecutorService、Callable、Future 實現有返回結果的多線程
四、啓動線程方法 start()和 run()有什麼區別?編程
- 只有調用了 start()方法,纔會表現出多線程的特性,不一樣線程的 run()方法裏面的代碼交替執行。若是隻是調用 run()方法,那麼代碼仍是同步執行的,必須等待一個線程的 run()方法裏面的代碼所有執行完畢以後,另一個線程才能夠執行其 run()方法裏面的代碼。
五、怎麼終止一個線程?如何優雅地終止線程?緩存
六、一個線程的生命週期有哪幾種狀態?它們之間如何流轉的?安全
- NEW:毫無疑問表示的是剛建立的線程,尚未開始啓動。
- RUNNABLE: 表示線程已經觸發 start()方式調用,線程正式啓動,線程處於運行中狀態。
- BLOCKED:表示線程阻塞,等待獲取鎖,如碰到 synchronized、lock 等關鍵字等佔用臨界區的狀況,一旦獲取到鎖就進行 RUNNABLE 狀態繼續運行。
- WAITING:表示線程處於無限制等待狀態,等待一個特殊的事件來從新喚醒,如經過wait()方法進行等待的線程等待一個 notify()或者 notifyAll()方法,經過 join()方法進行等待的線程等待目標線程運行結束而喚醒,一旦經過相關事件喚醒線程,線程就進入了 RUNNABLE 狀態繼續運行。
- TIMED_WAITING:表示線程進入了一個有時限的等待,如 sleep(3000),等待 3 秒後線程從新進行 RUNNABLE 狀態繼續運行。
- TERMINATED:表示線程執行完畢後,進行終止狀態。須要注意的是,一旦線程經過 start 方法啓動後就不再能回到初始 NEW 狀態,線程終止後也不能再回到 RUNNABLE 狀態
七、線程中的 wait()和 sleep()方法有什麼區別?服務器
- 這個問題常問,sleep 方法和 wait 方法均可以用來放棄 CPU 必定的時間,不一樣點在於若是線程持有某個對象的監視器,sleep 方法不會放棄這個對象的監視器,wait 方法會放棄這個對象的監視器
八、多線程同步有哪幾種方法?多線程
- Synchronized 關鍵字,Lock 鎖實現,分佈式鎖等。
九、什麼是死鎖?如何避免死鎖?
十、多線程之間如何進行通訊?
十一、線程怎樣拿到返回結果?
十二、violatile 關鍵字的做用?
- 一個很是重要的問題,是每一個學習、應用多線程的 Java 程序員都必須掌握的。理解 volatile關鍵字的做用的前提是要理解 Java 內存模型,這裏就不講 Java 內存模型了,能夠參見第31 點,volatile 關鍵字的做用主要有兩個:
- 一、多線程主要圍繞可見性和原子性兩個特性而展開,使用 volatile 關鍵字修飾的變量,保證了其在多線程之間的可見性,即每次讀取到 volatile 變量,必定是最新的數據
- 二、代碼底層執行不像咱們看到的高級語言----Java 程序這麼簡單,它的執行是 Java 代碼-->字節碼-->根據字節碼執行對應的 C/C++代碼-->C/C++代碼被編譯成彙編語言-->和硬件電路交互,現實中,爲了獲取更好的性能 JVM 可能會對指令進行重排序,多線程下可能會出現一些意想不到的問題。使用 volatile 則會對禁止語義重排序,固然這也必定程度上下降了代碼執行效率從實踐角度而言,volatile 的一個重要做用就是和 CAS 結合,保證了原子性,詳細的能夠參見 java.util.concurrent.atomic 包下的類,好比 AtomicInteger。
1三、新建 T一、T二、T3 三個線程,如何保證它們按順序執行?
1四、怎麼控制同一時間只有 3 個線程運行?
1五、爲何要使用線程池?
- 咱們知道不用線程池的話,每一個線程都要經過 new Thread(xxRunnable).start()的方式來建立並運行一個線程,線程少的話這不會是問題,而真實環境可能會開啓多個線程讓系統和程序達到最佳效率,當線程數達到必定數量就會耗盡系統的 CPU 和內存資源,也會形成 GC頻繁收集和停頓,由於每次建立和銷燬一個線程都是要消耗系統資源的,若是爲每一個任務都建立線程這無疑是一個很大的性能瓶頸。因此,線程池中的線程複用極大節省了系統資源,當線程一段時間再也不有任務處理時它也會自動銷燬,而不會長駐內存。
1六、經常使用的幾種線程池並講講其中的工做原理。
- 什麼是線程池?
很簡單,簡單看名字就知道是裝有線程的池子,咱們能夠把要執行的多線程交給線程池來處理,和鏈接池的概念同樣,經過維護必定數量的線程池來達到多個線程的複用。
- 線程池的好處
咱們知道不用線程池的話,每一個線程都要經過 new Thread(xxRunnable).start()的方式來建立並運行一個線程,線程少的話這不會是問題,而真實環境可能會開啓多個線程讓系統和程序達到最佳效率,當線程數達到必定數量就會耗盡系統的 CPU 和內存資源,也會形成 GC頻繁收集和停頓,由於每次建立和銷燬一個線程都是要消耗系統資源的,若是爲每一個任務都建立線程這無疑一個很大的性能瓶頸。因此,線程池中的線程複用極大節省了系統資源,當線程一段時間再也不有任務處理時它也會自動銷燬,而不會長駐內存。
- 線程池核心類
在 java.util.concurrent 包中咱們能找到線程池的定義,其中 ThreadPoolExecutor 是咱們線程池核心類,首先看看線程池類的主要參數有哪些。
- 如何提交線程
如能夠先隨便定義一個固定大小的線程池
ExecutorService es = Executors.newFixedThreadPool(3);
- 提交一個線程
es.submit(xxRunnble);
es.execute(xxRunnble);
- submit 和 execute 分別有什麼區別呢?
execute 沒有返回值,若是不須要知道線程的結果就使用 execute 方法,性能會好不少。submit 返回一個 Future 對象,若是想知道線程結果就使用 submit 提交,並且它能在主線程中經過 Future 的 get 方法捕獲線程中的異常。
- 如何關閉線程池
es.shutdown();
再也不接受新的任務,以前提交的任務等執行結束再關閉線程池。
es.shutdownNow();
再也不接受新的任務,試圖中止池中的任務再關閉線程池,返回全部未處理的線程 list 列表。
1七、線程池啓動線程 submit()和 execute()方法有什麼不一樣?
- execute 沒有返回值,若是不須要知道線程的結果就使用 execute 方法,性能會好不少。submit 返回一個 Future 對象,若是想知道線程結果就使用 submit 提交,並且它能在主線程中經過 Future 的 get 方法捕獲線程中的異常。
1八、CyclicBarrier 和 CountDownLatch 的區別?
- 兩個看上去有點像的類,都在 java.util.concurrent 下,均可以用來表示代碼運行到某個點上,兩者的區別在於:
- 一、CyclicBarrier 的某個線程運行到某個點上以後,該線程即中止運行,直到全部的線程都到達了這個點,全部線程才從新運行;CountDownLatch 則不是,某線程運行到某個點上以後,只是給某個數值-1 而已,該線程繼續運行
- 二、CyclicBarrier 只能喚起一個任務,CountDownLatch 能夠喚起多個任務
- 三、CyclicBarrier 可重用,CountDownLatch 不可重用,計數值爲 0 該 CountDownLatch就不可再用了
1九、什麼是活鎖、飢餓、無鎖、死鎖?
-
死鎖、活鎖、飢餓是關於多線程是否活躍出現的運行阻塞障礙問題,若是線程出現了
這三種狀況,即線程再也不活躍,不能再正常地執行下去了。
-
死鎖
死鎖是多線程中最差的一種狀況,多個線程相互佔用對方的資源的鎖,而又相互等對方釋放鎖,此時若無外力干預,這些線程則一直處理阻塞的假死狀態,造成死鎖。
舉個例子,A 同窗搶了 B 同窗的鋼筆,B 同窗搶了 A 同窗的書,兩我的都相互佔用對方的東西,都在讓對方先還給本身本身再還,這樣一直爭執下去等待對方還而又得不到解決,老師知道此過後就讓他們相互還給對方,這樣在外力的干預下他們才解決,固然這只是個例子沒有老師他們也能很好解決,計算機不像人若是發現這種狀況沒有外力干預仍是會一直阻塞下去的。
-
活鎖
活鎖這個概念你們應該不多有人據說或理解它的概念,而在多線程中這確實存在。活鎖偏偏與死鎖相反,死鎖是你們都拿不到資源都佔用着對方的資源,而活鎖是拿到資源卻又相互釋放不執行。當多線程中出現了相互謙讓,都主動將資源釋放給別的線程使用,這樣這個資源在多個線程之間跳動而又得不到執行,這就是活鎖。
-
飢餓
咱們知道多線程執行中有線程優先級這個東西,優先級高的線程可以插隊並優先執行,這樣若是優先級高的線程一直搶佔優先級低線程的資源,致使低優先級線程沒法獲得執行,這就是飢餓。固然還有一種飢餓的狀況,一個線程一直佔着一個資源不放而致使其餘線程得不到執行,與死鎖不一樣的是飢餓在之後一段時間內仍是可以獲得執行的,如那個佔用資源的線程結束了並釋放了資源。
- 無鎖
無鎖,即沒有對資源進行鎖定,即全部的線程都能訪問並修改同一個資源,但同時只有一個線程能修改爲功。無鎖典型的特色就是一個修改操做在一個循環內進行,線程會不斷的嘗試修改共享資源,若是沒有衝突就修改爲功並退出不然就會繼續下一次循環嘗試。因此,若是有多個線程修改同一個值一定會有一個線程能修改爲功,而其餘修改失敗的線程會不斷重試直到修改爲功。以前的文章我介紹過 JDK 的 CAS 原理及應用便是無鎖的實現。
能夠看出,無鎖是一種很是良好的設計,它不會出現線程出現的跳躍性問題,鎖使用不當確定會出現系統性能問題,雖然無鎖沒法全面代替有鎖,但無鎖在某些場合下是很是高效的。
20、什麼是原子性、可見性、有序性?
-
原子性、可見性、有序性是多線程編程中最重要的幾個知識點,因爲多線程狀況複雜,如何讓每一個線程能看到正確的結果,這是很是重要的。
-
原子性
原子性是指一個線程的操做是不能被其餘線程打斷,同一時間只有一個線程對一個變量進行操做。在多線程狀況下,每一個線程的執行結果不受其餘線程的干擾,好比說多個線程同時對同一個共享成員變量 n++100 次,若是 n 初始值爲 0,n 最後的值應該是 100,因此說它們是互不干擾的,這就是傳說的中的原子性。但 n++並非原子性的操做,要使用 AtomicInteger 保證原子性。
-
可見性
可見性是指某個線程修改了某一個共享變量的值,而其餘線程是否能夠看見該共享變量修改後的值。在單線程中確定不會有這種問題,單線程讀到的確定都是最新的值,而在多線程編程中就不必定了。
每一個線程都有本身的工做內存,線程先把共享變量的值從主內存讀到工做內存,造成一個副本,當計算完後再把副本的值刷回主內存,從讀取到最後刷回主內存這是一個過程,當還沒刷回主內存的時候這時候對其餘線程是不可見的,因此其餘線程從主內存讀到的值是修改以前的舊值。像 CPU 的緩存優化、硬件優化、指令重排及對 JVM 編譯器的優化,都會出現可見性的問題。
-
有序性
咱們都知道程序是按代碼順序執行的,對於單線程來講確實是如此,但在多線程狀況下就不是如此了。爲了優化程序執行和提升 CPU 的處理性能,JVM 和操做系統都會對指令進行重排,也就說前面的代碼並不必定都會在後面的代碼前面執行,即後面的代碼可能會插到前面的代碼以前執行,只要不影響當前線程的執行結果。因此,指令重排只會保證當前線程執行結果一致,但指令重排後勢必會影響多線程的執行結果。雖然重排序優化了性能,但也是會遵照一些規則的,並不能隨便亂排序,只是重排序會影響多線程執行的結果。
- 什麼是守護線程?
與守護線程相對應的就是用戶線程,守護線程就是守護用戶線程,當用戶線程所有執行完結束以後,守護線程纔會跟着結束。也就是守護線程必須伴隨着用戶線程,若是一個應用內只存在一個守護線程,沒有用戶線程,守護線程天然會退出。
2二、一個線程運行時發生異常會怎樣?
- 若是異常沒有被捕獲該線程將會中止執行。Thread.UncaughtExceptionHandler 是用於處理未捕獲異常形成線程忽然中斷狀況的一個內嵌接口。當一個未捕獲異常將形成線程中斷的時 候 JVM 會 使 用 Thread.getUncaughtExceptionHandler() 來 查 詢 線 程 的UncaughtExceptionHandler 並 將 線 程 和 異 常 做 爲 參 數 傳 遞 給 handler 的uncaughtException()方法進行處理。
2三、線程 yield()方法有什麼用?
- Yield 方法能夠暫停當前正在執行的線程對象,讓其它有相同優先級的線程執行。它是一個靜態方法並且只保證當前線程放棄 CPU 佔用而不能保證使其它線程必定能佔用 CPU,執行yield()的線程有可能在進入到暫停狀態後立刻又被執行。
2四、什麼是重入鎖?
- 所謂重入鎖,指的是以線程爲單位,當一個線程獲取對象鎖以後,這個線程能夠再次獲取本對象上的鎖,而其餘的線程是不能夠的。
2五、Synchronized 有哪幾種用法?
2六、Fork/Join 框架是幹什麼的?
2七、線程數過多會形成什麼異常?
2八、說說線程安全的和不安全的集合。
- Java 中平時用的最多的 Map 集合就是 HashMap 了,它是線程不安全的。
- 看下面兩個場景:
- 一、當用在方法內的局部變量時,局部變量屬於當前線程級別的變量,其餘線程訪問不了,因此這時也不存在線程安全不安全的問題了。
- 二、當用在單例對象成員變量的時候呢?這時候多個線程過來訪問的就是同一個HashMap 了,對同個 HashMap 操做這時候就存在線程安全的問題了。
2九、什麼是 CAS 算法?在多線程中有哪些應用。
- CAS,全稱爲 Compare and Swap,即比較-替換。假設有三個操做數:內存值 V、舊的預期值 A、要修改的值 B,當且僅當預期值 A 和內存值 V 相同時,纔會將內存值修改成 B 並返回 true,不然什麼都不作並返回 false。固然 CAS 必定要 volatile 變量配合,這樣才能保證每次拿到的變量是主內存中最新的那個值,不然舊的預期值 A 對某條線程來講,永遠是一個不會變的值 A,只要某次 CAS 操做失敗,永遠都不可能成功。java.util.concurrent.atomic 包下面的 Atom****類都有 CAS 算法的應用。
30、怎麼檢測一個線程是否擁有鎖?
- java.lang.Thread#holdsLock 方法
3一、Jdk 中排查多線程問題用什麼命令?
3二、線程同步須要注意什麼?
- 一、儘可能縮小同步的範圍,增長系統吞吐量。
- 二、分佈式同步鎖無心義,要使用分佈式鎖。
- 三、防止死鎖,注意加鎖順序。
3三、線程 wait()方法使用有什麼前提?
3四、Fork/Join 框架使用有哪些要注意的地方?
- 若是任務拆解的很深,系統內的線程數量堆積,致使系統性能性能嚴重降低;
- 若是函數的調用棧很深,會致使棧內存溢出;
3五、線程之間如何傳遞數據?
- 通 過 在 線 程 之 間 共 享 對 象 就 可 以 了 , 然 後 通 過 wait/notify/notifyAll 、await/signal/signalAll 進行喚起和等待,比方說阻塞隊列 BlockingQueue 就是爲線程之間共享數據而設計的
3六、保證"可見性"有哪幾種方式?
3七、說幾個經常使用的 Lock 接口實現鎖。
- ReentrantLock、ReadWriteLock
3八、ThreadLocal 是什麼?有什麼應用場景?
- ThreadLocal 的做用是提供線程內的局部變量,這種變量在線程的生命週期內起做用,減小同一個線程內多個函數或者組件之間一些公共變量的傳遞的複雜度。用來解決數據庫鏈接、Session 管理等。
3九、ReadWriteLock 有什麼用?
- ReadWriteLock 是一個讀寫鎖接口,ReentrantReadWriteLock 是 ReadWriteLock 接口的一個具體實現,實現了讀寫的分離,讀鎖是共享的,寫鎖是獨佔的,讀和讀之間不會互斥,讀和寫、寫和讀、寫和寫之間纔會互斥,提高了讀寫的性能。
40、FutureTask 是什麼?
- FutureTask 表示一個異步運算的任務,FutureTask 裏面能夠傳入一個 Callable 的具體實現類,能夠對這個異步運算的任務的結果進行等待獲取、判斷是否已經完成、取消任務等操做。
4一、怎麼喚醒一個阻塞的線程?
- 若是線程是由於調用了 wait()、sleep()或者 join()方法而致使的阻塞,能夠中斷線程,而且經過拋出 InterruptedException 來喚醒它;若是線程遇到了 IO 阻塞,無能爲力,由於 IO是操做系統實現的,Java 代碼並無辦法直接接觸到操做系統。
4二、不可變對象對多線程有什麼幫助?
- 不可變對象保證了對象的內存可見性,對不可變對象的讀取不須要進行額外的同步手段,提
升了代碼執行效率。
4三、多線程上下文切換是什麼意思?
- 多線程的上下文切換是指 CPU 控制權由一個已經正在運行的線程切換到另一個就緒並等待獲取 CPU 執行權的線程的過程。
4四、Java 中用到了什麼線程調度算法?
- 搶佔式。一個線程用完 CPU 以後,操做系統會根據線程優先級、線程飢餓狀況等數據算出一個總的優先級並分配下一個時間片給某個線程執行。
4五、Thread.sleep(0)的做用是什麼?
- 因爲 Java 採用搶佔式的線程調度算法,所以可能會出現某條線程經常獲取到 CPU 控制權的狀況,爲了讓某些優先級比較低的線程也能獲取到 CPU 控制權,可使用 Thread.sleep(0)手動觸發一次操做系統分配時間片的操做,這也是平衡 CPU 控制權的一種操做。
4六、什麼是樂觀鎖和悲觀鎖?
- 樂觀鎖:就像它的名字同樣,對於併發間操做產生的線程安全問題持樂觀狀態,樂觀鎖認爲競爭不老是會發生,所以它不須要持有鎖,將比較-替換這兩個動做做爲一個原子操做嘗試去修改內存中的變量,若是失敗則表示發生衝突,那麼就應該有相應的重試邏輯。
- 悲觀鎖:仍是像它的名字同樣,對於併發間操做產生的線程安全問題持悲觀狀態,悲觀鎖認爲競爭老是會發生,所以每次對某資源進行操做時,都會持有一個獨佔的鎖,就像synchronized,無論三七二十一,直接上了鎖就操做資源了。
4七、Hashtable 的 size()方法爲何要作同步?
- 同一時間只能有一條線程執行固定類的同步方法,可是對於類的非同步方法,能夠多條線程同時訪問。因此,這樣就有問題了,可能線程 A 在執行 Hashtable 的 put 方法添加數據,線程 B 則能夠正常調用 size()方法讀取 Hashtable 中當前元素的個數,那讀取到的值可能不是最新的,可能線程 A 添加了完了數據,可是沒有對 size++,線程 B 就已經讀取 size了,那麼對於線程 B 來講讀取到的 size 必定是不許確的。而給 size()方法加了同步以後,意味着線程 B 調用 size()方法只有在線程 A 調用 put 方法完畢以後才能夠調用,這樣就保證了線程安全性CPU 執行代碼,執行的不是 Java 代碼,這點很關鍵,必定得記住。Java 代碼最終是被翻譯成機器碼執行的,機器碼纔是真正能夠和硬件電路交互的代碼。即便你看到 Java 代碼只有一行,甚至你看到 Java 代碼編譯以後生成的字節碼也只有一行,也不意味着對於底層來講這句語句的操做只有一個。一句"return count"假設被翻譯成了三句彙編語句執行,一句彙編語句和其機器碼作對應,徹底可能執行完第一句,線程就切換了。
4八、同步方法和同步塊,哪一種更好?
- 同步塊,這意味着同步塊以外的代碼是異步執行的,這比同步整個方法更提高代碼的效率。請知道一條原則:同步的範圍越小越好。
4九、什麼是自旋鎖?
- 自旋鎖是採用讓當前線程不停地的在循環體內執行實現的,當循環的條件被其餘線程改變時
才能進入臨界區。
50、Runnable 和 Thread 用哪一個好?
- Java 不支持類的多重繼承,但容許你實現多個接口。因此若是你要繼承其餘類,也爲了減
少類之間的耦合性,Runnable 會更好。
5一、Java 中 notify 和 notifyAll 有什麼區別?
- notify()方法不能喚醒某個具體的線程,因此只有一個線程在等待的時候它纔有用武之地。而 notifyAll()喚醒全部線程並容許他們爭奪鎖確保了至少有一個線程能繼續運行。
5二、爲何 wait/notify/notifyAll 這些方法不在 thread 類裏面?
- 這是個設計相關的問題,它考察的是面試者對現有系統和一些廣泛存在但看起來不合理的事物的見解。回答這些問題的時候,你要說明爲何把這些方法放在 Object 類裏是有意義的,還有不把它放在 Thread 類裏的緣由。一個很明顯的緣由是 JAVA 提供的鎖是對象級的而不是線程級的,每一個對象都有鎖,經過線程得到。若是線程須要等待某些鎖那麼調用對象中的wait()方法就有意義了。若是 wait()方法定義在 Thread 類中,線程正在等待的是哪一個鎖就不明顯了。簡單的說,因爲 wait,notify 和 notifyAll 都是鎖級別的操做,因此把他們定義在 Object 類中由於鎖屬於對象。
5三、爲何 wait 和 notify 方法要在同步塊中調用?
- 主 要 是 因 爲 Java API 強 制 要 求 這 樣 作 , 如 果 你 不 這 麼 作 , 你 的 代 碼 會 拋 出IllegalMonitorStateException 異常。還有一個緣由是爲了不 wait 和 notify 之間產生競態條件。
5四、爲何你應該在循環中檢查等待條件?
- 處於等待狀態的線程可能會收到錯誤警報和僞喚醒,若是不在循環中檢查等待條件,程序就會在沒有知足結束條件的狀況下退出。所以,當一個等待線程醒來時,不能認爲它原來的等待狀態仍然是有效的,在 notify()方法調用以後和等待線程醒來以前這段時間它可能會改變。這就是在循環中使用 wait()方法效果更好的緣由,你能夠在 Eclipse 中建立模板調用 wait和 notify 試一試。
5五、Java 中堆和棧有什麼不一樣?
- 每一個線程都有本身的棧內存,用於存儲本地變量,方法參數和棧調用,一個線程中存儲的變量對其它線程是不可見的。而堆是全部線程共享的一片公用內存區域。對象都在堆裏建立,爲了提高效率線程會從堆中弄一個緩存到本身的棧,若是多個線程使用該變量就可能引起問題,這時 volatile 變量就能夠發揮做用了,它要求線程從主存中讀取變量的值。
5六、你如何在 Java 中獲取線程堆棧?
- 對於不一樣的操做系統,有多種方法來得到 Java 進程的線程堆棧。當你獲取線程堆棧時,JVM會把全部線程的狀態存到日誌文件或者輸出到控制檯。在 Windows 你可使用 Ctrl +Break 組合鍵來獲取線程堆棧,Linux 下用 kill -3 命令。你也能夠用 jstack 這個工具來獲取,它對線程 id 進行操做,你能夠用 jps 這個工具找到 id。
5七、如何建立線程安全的單例模式?
- 單例模式即一個 JVM 內存中只存在一個類的對象實例分類
- 一、懶漢式
類加載的時候就建立實例
- 二、餓漢式
使用的時候才建立實例
5八、什麼是阻塞式方法?
- 阻塞式方法是指程序會一直等待該方法完成期間不作其餘事情,ServerSocket 的 accept()方法就是一直等待客戶端鏈接。這裏的阻塞是指調用結果返回以前,當前線程會被掛起,直到獲得結果以後纔會返回。此外,還有異步和非阻塞式方法在任務完成前就返回。
5九、提交任務時線程池隊列已滿會時發會生什麼?
- 當線程數小於最大線程池數 maximumPoolSize 時就會建立新線程來處理,而線程數大於等於最大線程池數 maximumPoolSize 時就會執行拒絕策略。