1、如何在Java中實現線程(4種)?
1.繼承Thread類,重寫run方法(其實Thread類本身也實現了Runnable接口)
2.實現Runnable接口,重寫run方法
3.實現Callable接口,重寫call方法(有返回值)
4.使用線程池(有返回值)
https://www.cnblogs.com/duanjiapingjy/p/9434244.htmlhttps://www.cnblogs.com/duanjiapingjy/p/9434244.html
2、在具體多線程編程實踐中,如何選用Runnable還是Thread?
Java中實現多線程有兩種方法:繼承Thread類、實現Runnable接口,在程序開發中只要是多線程,肯定永遠以實現Runnable接口為主,因為實現Runnable接口相比繼承Thread類有如下優勢:
1、可以避免由於Java的單繼承特性而帶來的局限;
2、增強程序的健壯性,代碼能夠被多個線程共享,代碼與數據是獨立的;
適合多個相同程序代碼的線程區處理同一資源的情況。
3、Thread類中的start()和run()方法有什么區別?
start()方法來啟動線程,真正實現了多線程運行,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的代碼: 通過調用Thread類的start()方法來啟動一個線程,這時此線程是處於就緒狀態,並沒有運行。然后通過此Thread類調用方法run()來完成其運行操作的,這里方法run()稱為線程體,它包含了要執行的這個線程的內容,Run方法運行結束,此線程終止,而CPU再運行其它線程。
run()方法當作普通方法的方式調用,程序還是要順序執行,還是要等待run方法體執行完畢后才可繼續執行下面的代碼: 而如果直接用run方法,這只是調用一個方法而已,程序中依然只有主線程–這一個線程,其程序執行路徑還是只有一條,這樣就沒有達到多線程的目的。
4、Java中Runnable和Callable有什么不同
相同點:
1. 兩者都是接口;(廢話)
2. 兩者都可用來編寫多線程程序;
3. 兩者都需要調用Thread.start()啟動線程;
不同點:
1. 兩者最大的不同點是:實現Callable接口的任務線程能返回執行結果;而實現Runnable接口的任務線程不能返回結果;
2. Callable接口的call()方法允許拋出異常;而Runnable接口的run()方法的異常只能在內部消化,不能繼續上拋;
注意點:
Callable接口支持返回執行結果,此時需要調用FutureTask.get()方法實現,此方法會阻塞主線程直到獲取‘將來’結果;當不調用此方法時,主線程不會阻塞!
5、如何避免死鎖?
1. 加鎖順序
按照順序加鎖是一種有效的死鎖預防機制。但是,這種方式需要你事先知道所有可能會用到的鎖(並對這些鎖做適當的排序),但總有些時候是無法預知的。
2. 加鎖時限
另外一個可以避免死鎖的方法是在嘗試獲取鎖的時候加一個超時時間,這也就意味着在嘗試獲取鎖的過程中若超過了這個時限該線程則放棄對該鎖請求。
3.死鎖檢測
死鎖檢測是一個更好的死鎖預防機制,它主要是針對那些不可能實現按序加鎖並且鎖超時也不可行的場景。
每當一個線程獲得了鎖,會在線程和鎖相關的數據結構中(map、graph等等)將其記下。除此之外,每當有線程請求鎖,也需要記錄在這個數據結構中。
當一個線程請求鎖失敗時,這個線程可以遍歷鎖的關系圖看看是否有死鎖發生。例如,線程A請求鎖7,但是鎖7這個時候被線程B持有,這時線程A就可以檢查一下線程B是否已經請求了線程A當前所持有的鎖。如果線程B確實有這樣的請求,那么就是發生了死鎖(線程A擁有鎖1,請求鎖7;線程B擁有鎖7,請求鎖1)。
當然,死鎖一般要比兩個線程互相持有對方的鎖這種情況要復雜的多。線程A等待線程B,線程B等待線程C,線程C等待線程D,線程D又在等待線程A。線程A為了檢測死鎖,它需要遞進地檢測所有被B請求的鎖。從線程B所請求的鎖開始,線程A找到了線程C,然后又找到了線程D,發現線程D請求的鎖被線程A自己持有着。這是它就知道發生了死鎖。
6、Java多線程中調用wait() 和 sleep()方法有什么不同?
共同點:
1. 他們都是在多線程的環境下,都可以在程序的調用處阻塞指定的毫秒數,並返回。
2. wait()和sleep()都可以通過interrupt()方法 打斷線程的暫停狀態 ,從而使線程立刻拋出InterruptedException。
如果線程A希望立即結束線程B,則可以對線程B對應的Thread實例調用interrupt方法。如果此刻線程B正在wait/sleep /join,則線程B會立刻拋出InterruptedException,在catch() {} 中直接return即可安全地結束線程。
需要注意的是,InterruptedException是線程自己從內部拋出的,並不是interrupt()方法拋出的。對某一線程調用 interrupt()時,如果該線程正在執行普通的代碼,那么該線程根本就不會拋出InterruptedException。但是,一旦該線程進入到 wait()/sleep()/join()后,就會立刻拋出InterruptedException 。
不同點:
1. Thread類的方法:sleep(),yield()等
Object的方法:wait()和notify()等
2. 每個對象都有一個鎖來控制同步訪問。Synchronized關鍵字可以和對象的鎖交互,來實現線程的同步。
sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。
3. wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用
4. sleep必須捕獲異常,而wait,notify和notifyAll不需要捕獲異常
7、什么是Executor框架
我們知道線程池就是線程的集合,線程池集中管理線程,以實現線程的重用,降低資源消耗,提高響應速度等。線程用於執行異步任務,單個的線程既是工作單元也是執行機制,從JDK1.5開始,為了把工作單元與執行機制分離開,Executor框架誕生了,他是一個用於統一創建與運行的接口。Executor框架實現的就是線程池的功能。
8、在Java中Executor和Executors的區別
Executor是http://lib.csdn.net/base/javaee" \o "Java EE知識庫" \t "https://blog.csdn.net/qq991029781/article/details/_blankJava線程池的頂級接口;
Executors是一個類, Executors類提供了若干個靜態方法,用於生成不同類型的線程池:
9、什么是多線程中的上下文切換?
即使是單核CPU也支持多線程執行代碼,CPU通過給每個線程分配CPU時間片來實現這個機制。時間片是CPU分配給各個線程的時間,因為時間片非常短,所以CPU通過不停地切換線程執行,讓我們感覺多個線程時同時執行的,時間片一般是幾十毫秒(ms)。
CPU通過時間片分配算法來循環執行任務,當前任務執行一個時間片后會切換到下一個任務。但是,在切換前會保存上一個任務的狀態,以便下次切換回這個任務時,可以再次加載這個任務的狀態,從任務保存到再加載的過程就是一次上下文切換。
這就像我們同時讀兩本書,當我們在讀一本英文的技術書籍時,發現某
10、什么是線程安全
線程安全的代碼是多個線程同時執行也能工作的代碼
如果一段代碼可以保證多個線程訪問的時候正確操作共享數據,那么它是線程安全的
如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,
就是線程安全的。
或者說:一個類或者程序所提供的接口對於線程來說是原子操作或者多個線程之間的切換不會導致該接口的執行結果存在二義性,也就是說我們不用考慮同步的問題。
11、如何檢測死鎖?怎么預防死鎖?
利用Java自帶工具 https://www.baidu.com/s?wd=JConsole&tn=24004469_oem_dg&rsv_dl=gh_pl_sl_csd" \t "https://blog.csdn.net/wolfcode_cn/article/details/_blankJConsole.界面化查看死鎖。
利用jstack 命令檢測死鎖 jpg jstack -l pid >wenjian.txt
1. 破壞“不可剝奪”條件:一個進程不能獲得所需要的全部資源時便處於等待狀態,等待期間他占有的資源將被隱式的釋放重新加入到 系統的資源列表中,可以被其他的進程使用,而等待的進程只有重新獲得自己原有的資源以及新申請的資源才可以重新啟動,執行。
2. 破壞”請求與保持條件“:第一種方法靜態分配即每個進程在開始執行時就申請他所需要的全部資源。第二種是動態分配即每個進程在申請所需要的資源時他本身不占用系統資源。
3. 破壞“循環等待”條件:采用資源有序分配其基本思想是將系統中的所有資源順序編號,將緊缺的,稀少的采用較大的編號,在申請資源時必須按照編號的順序進行,一個進程只有獲得較小編號的進程才能申請較大編號的進程。
12、Java中用到的線程調度算法是什么
搶占式。一個線程用完CPU之后,操作系統會根據線程優先級、線程飢餓情況等數據算出一個總的優先級並分配下一個時間片給某個線程執行
(時間片輪轉法、優先級調度法、多級反饋隊列調度法等
13、Java中如何獲取到線程dump文件
jmap -dump:format=b,file=F:/heamdump.out 16540”命令即可以生成
jpg jstack -l pid >wenjian.txt
14、池技術有什么作用,常見的池技術有哪些
起到對象復用
15、用線程池有什么好處,請談談線程池的使用場景
1、避免重復創建線程,減少在創建和 銷毀線程時所花時間,及系統的整體開銷
2、避免系統創建大量線程而消耗系統資源
3、用戶提交的數據能夠及時得到處理,響應速度快
4、能夠更好的監控和管理線程
常量池 線程池 數據庫連接池
16、線程池的技術原理是什么
預先啟動一些線程,線程無限循環從任務隊列中獲取一個任務進行執行,直到線程池被關閉。如果某個線程因為執行某個任務發生異常而終止,那么重新創建一個新的線程而已。如此反復。
17、線程池有哪些種類,各自的使用場景是什么?
newCachedThreadPool:
底層:返回ThreadPoolExecutor實例,corePoolSize為0;maximumPoolSize為Integer.MAX_VALUE;keepAliveTime為60L;unit為TimeUnit.SECONDS;workQueue為SynchronousQueue(同步隊列)
通俗:當有新任務到來,則插入到SynchronousQueue中,由於SynchronousQueue是同步隊列,因此會在池中尋找可用線程來執行,若有可以線程則執行,若沒有可用線程則創建一個線程來執行該任務;若池中線程空閑時間超過指定大小,則該線程會被銷毀。
適用:執行很多短期異步的小程序或者負載較輕的服務器
newFixedThreadPool:
底層:返回ThreadPoolExecutor實例,接收參數為所設定線程數量nThread,corePoolSize為nThread,maximumPoolSize為nThread;keepAliveTime為0L(不限時);unit為:TimeUnit.MILLISECONDS;WorkQueue為:new LinkedBlockingQueue<Runnable>() 無解阻塞隊列
通俗:創建可容納固定數量線程的池子,每隔線程的存活時間是無限的,當池子滿了就不在添加線程了;如果池中的所有線程均在繁忙狀態,對於新任務會進入阻塞隊列中(無界的阻塞隊列)
適用:執行長期的任務,性能好很多
newSingleThreadExecutor:
底層:FinalizableDelegatedExecutorService包裝的ThreadPoolExecutor實例,corePoolSize為1;maximumPoolSize為1;keepAliveTime為0L;unit為:TimeUnit.MILLISECONDS;workQueue為:new LinkedBlockingQueue<Runnable>() 無解阻塞隊列
通俗:創建只有一個線程的線程池,且線程的存活時間是無限的;當該線程正繁忙時,對於新任務會進入阻塞隊列中(無界的阻塞隊列)
適用:一個任務一個任務執行的場景
NewScheduledThreadPool:
底層:創建ScheduledThreadPoolExecutor實例,corePoolSize為傳遞來的參數,maximumPoolSize為Integer.MAX_VALUE;keepAliveTime為0;unit為:TimeUnit.NANOSECONDS;workQueue為:new DelayedWorkQueue() 一個按超時時間升序排序的隊列
通俗:創建一個固定大小的線程池,線程池內線程存活時間無限制,線程池可以支持定時及周期性任務執行,如果所有線程均處於繁忙狀態,對於新任務會進入DelayedWorkQueue隊列中,這是一種按照超時時間排序的隊列結構
適用:周期性執行任務的場景
18、線程池有哪些重要的參數?
a.核心線程數
b 最大線程數
c 線程空閑時間
c 時間單位
d 阻塞隊列大小:queueCapacity
e 任務拒絕處理器 :rejectedExceptionHandler
19、你們在具體的設計開發過程中是如何設置這些重要參數的?
根據任務的特性具體方案 具體定制 參見17
20、單例的使用場景是什么,如何實現單例
系統中只存在一個實力,一種是枚舉,還有一種私有靜態內部類
單例對象的類必須保證只有一個實例存在。許多時候整個系統只需要擁有一個的全局對象,這樣有利於我們協調系統整體的行為
21、如何在Java中創建線程安全的Singleton
public class StaticSingleton {
02 private StaticSingleton(){
03 System.out.println("StaticSingleton is create");
04 }
05 private static class SingletonHolder {
06 private static StaticSingleton instance = new StaticSingleton();
07 }
08 public static StaticSingleton getInstance() {
09 return SingletonHolder.instance;
10 }
22、11 }
23、synchronzied關鍵詞的使用
synchronized包裹代碼塊:
I . synchronized(對象){}
II . synchronized(類名.class){}
III. synchronized(this){}
synchronized修飾方法:
I .public synchronized void memberMethod(){};
II.public static synchronized void staticMethod(){};
24、ReentrantLock和synchronized使用的場景是什么,機制有何不同
在資源競爭不是很激烈的情況下,偶爾會有同步的情形下,synchronized是很合適的。原因在於,編譯程序通常會盡可能的進行優化
synchronize,另外可讀性非常好,不管用沒用過5.0多線程包的程序員都能理解。
ReentrantLock 類實現了 Lock ,它擁有與 synchronized 相同的並發性和內存語義,但是添加了類似輪詢鎖、定時鎖等候和可中斷鎖等候的一些特性。此外,它還提供了在激烈爭用情況下更佳
的性能。
其實ReentrantLock是一個可重入的互斥鎖,重入鎖是一種遞歸無阻塞的同步機制。ReentrantLock由最近成功獲取鎖,還沒有釋放的線程所擁有,當鎖被另一個線程擁有時,調用lock的線程可以成功獲取鎖。如果鎖已經被當前線程擁有,當前線程會立即返回
ReentrantLock可以等同於synchronized使用,但是比synchronized有更強的功能、可以提供更靈活的鎖機制、同時減少死鎖的發生概率。在確實需要一些 synchronized 所沒有的特性的時候,比如時間鎖等候、可中斷鎖等候、無塊結構鎖、多個條件變量或者輪詢鎖。 ReentrantLock 還具有可伸縮性的好處,應當在高度爭用的情況下使用它,但是請記住,大多數 synchronized 塊幾乎從來沒有出現過爭用,所以可以把高度爭用放在一邊。我建議用 synchronized 開發,直到確實證明 synchronized 不合適,而不要僅僅是假設如果使用 ReentrantLock “性能會更好”。請記住,這些是供高級用戶使用的高級工具。(而且,真正的高級用戶喜歡選擇能夠找到的最簡單工具,直到他們認為
25、什么是ThreadLocal變量
ThreadLocal,Thread:線程,這個毫無疑問。那Local呢?本地的,局部的。也就是說,ThreadLocal是線程本地的變量,只要是本線程內都可以使用,線程結束了,那么相應的線程本地變量也就跟隨着線程消失了。
26、ThreadLocal技術原理是什么,它在架構中常常用來做什么?
27、java多線程有哪些常見的鎖,各自用法是什么?
synchronized (同步)synchronized關鍵字修飾的代碼相當於數據庫上的互斥鎖。確保多個線程在同一時刻只能由一個線程處於方法或同步塊中,確保線程對變量訪問的可見和排它,獲得鎖的對象在代碼結束后,會對鎖進行釋放。
synchronzied使用方法有兩個:①加在方法上面鎖定方法,②定義synchronized塊。
condition (配合lock使用 類似 object.wait)Condition.await()方法相當於Object.wait()方法,而Condition.signal()方法相當於Object.notify()方法。當然它也有對應的Condition.signalAll()方法。同樣的在調用Condition.await()之后,線程占用的鎖會被釋放。這樣在Condition.signal()方法調用的時候才獲取到鎖。
需要注意的是Condition.signal()方法調用之后,被喚醒的線程因為需要重新獲取鎖。所以需要等到調用Condition.signal()的線程釋放了鎖(調用ReentrantLock.unlock())之后才能繼續執行。
lockInterruptibly()方法比較特殊,當通過這個方法去獲取鎖時,如果線程正在等待獲取鎖,則這個線程能夠響應中斷,即中斷線程的等待狀態。也就使說,當兩個線程同時通過lock.lockInterruptibly()想獲取某個鎖時,假若此時線程A獲取到了鎖,而線程B只有在等待,那么對線程B調用threadB.interrupt()方法能夠中斷線程B的等待過程。由於lockInterruptibly()的聲明中拋出了異常,所以lock.lockInterruptibly()必須放在try塊中或者在調用lockInterruptibly()的方法外聲明拋出InterruptedException。
ReentrantLock,意思是“可重入鎖”,ReentrantLock是唯一實現了Lock接口的類,並且ReentrantLock提供了更多的方法。
分為讀鎖和寫鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥,這是由jvm自己控制的,我們只要上好相應的鎖即可。如果你的代碼只讀數據,可以很多人同時讀,但不能同時寫,那就上讀鎖;如果你的代碼修改數據,只能有一個人在寫,且不能同時讀取,那就上寫鎖。總之,讀的時候上讀鎖,寫的時候上寫鎖!讀寫鎖接口:ReadWriteLock,它的具體實現類為:ReentrantReadWriteLock。
28、CountDownLatch用於多線程的什么場景
1. ountDownLatch類。這個類是一個同步輔助類。用於一個線程等待多個操作完成之后再執行,也就是這個當前線程會一直阻塞,直到它所等待的多個操作已經完成。await方法,需要等到其他操作先完成的那個線程調用的,先將線程休眠,直到其他操作完成,計數器減為0,才會喚醒因此休眠的線程
2. countDown方法,每個被等待的事件在完成之后調用,會將計數器減一
29、volatile適用於高並發的什么場景
volatile最適用一個線程寫,多個線程讀的場合。
如果有多個線程並發寫操作,仍然需要使用鎖或者線程安全的容器或者原子變量來代替。(摘自Netty權威指南)
您只能在有限的一些情形下使用 volatile 變量替代鎖。要使 volatile 變量提供理想的線程安全,必須同時滿足下面兩個條件:
對變量的寫操作不依賴於當前值。
該變量沒有包含在具有其他變量的不變式中。
30、多線程join方法用於什么場景?
,主線程創建並啟動了子線程,如果子線程中需要進行大量的耗時運算,主線程往往將早於子線程結束之前結束,如果主線程想等待子線程執行完畢后,獲得子線程中的處理完的某個數據,就要用到join方法了,方法join()的作用是等待線程對象唄銷毀;
join底層是wait方法,所以它是會釋放https://www.baidu.com/s?wd=%E5%AF%B9%E8%B1%A1%E9%94%81&tn=24004469_oem_dg&rsv_dl=gh_pl_sl_csd" \t "https://blog.csdn.net/liuyifeng1920/article/details/_blank對象鎖的,而sleep在同步的方法中是不釋放對象鎖的,只有同步方法執行完畢,其他線程才可以執行。
31、java多線程中讓所有子線程執行完畢的方法有哪幾種?
1、用sleep方法,讓主線程睡眠一段時間,當然這個睡眠時間是主觀的時間,是我們自己定的,這個方法不推薦,但是在這里還是寫一下,畢竟是解決方法
2、使用Thread的join()等待所有的子線程執行完畢,主線程在執行,thread.join()把指定的線程加入到當前線程,可以將兩個交替執行的線程合並為順序執行的線程。比如在線程B中調用了線程A的Join()方法,直到線程A執行完畢后,才會繼續執行線程B。
3、countDownLatch不可能重新初始化或者修改CountDownLatch對象內部計數器的值,一個線程調用countdown方法happen-before另外一個線程調用await方法
4、同步屏障CyclicBarrier方法可以使用reset()方法重置,所以CyclicBarrier方法可以能處理更為復雜的業務場景。
32、高並發環境下的計數器如何實現?
33、HashTable、HashMap、ConcurrentHashMap各自的技術原理和使用場景是什么?
HashMap
實現了Map接口,實現了將唯一鍵隱射到特定值上。允許一個NULL鍵和多個NULL值。非線程安全。
HashTable
類似於HashMap,但是不允許NULL鍵和NULL值,比HashMap慢,因為它是同步的。HashTable是一個線程安全的類,它使用synchronized來鎖住整張Hash表來實現線程安全,即每次鎖住整張表讓線程獨占。
ConcurrentHashMap
ConcurrentHashMap允許多個修改操作並發進行,其關鍵在於使用了鎖分離技術。它使用了多個鎖來控制對hash表的不同部分進行的修改。ConcurrentHashMap內部使用段(Segment)來表示這些不同的部分,每個段其實就是一個小的Hashtable,它們有自己的鎖。只要多個修改操作發生在不同的段上,它們就可以並發進行。
34、LinkedBlockingQueue、ConcurrentLinkedQueue各自技術原理和使用場景是什么?
LinkedBlockingQueue是一個線程安全的阻塞隊列,基於鏈表實現,一般用於生產者與消費者模型的開發中。采用鎖機制來實現多線程同步,提供了一個構造方法用來指定隊列的大小,如果不指定大小,隊列采用默認大小(Integer.MAX_VALUE,即整型最大值)。
ConcurrentLinkedQueue是一個線程安全的非阻塞隊列,基於鏈表實現。java並沒有提供構造方法來指定隊列的大小,因此它是無界的。為了提高並發量,它通過使用更細的鎖機制,使得在多線程環境中只對部分數據進行鎖定,從而提高運行效率
35、Java中如何停止一個線程?
使用退出標志,使線程正常退出,也就是當run方法完成后線程終止。
2. 使用stop方法強行終止線程(這個方法不推薦使用,因為stop和suspend、resume一樣,也可能發生不可預料的結果)。
1. 3. 使用interrupt方法中斷線程。
36、Java中Semaphore是什么?
Semaphore是用來保護一個或者多個共享資源的訪問,Semaphore內部維護了一個計數器,其值為可以訪問的共享資源的個數。一個線程要訪問共享資源,先獲得信號量,如果信號量的計數器值大於1,意味着有共享資源可以訪問,則使其計數器值減去1,再訪問共享資源。
如果計數器值為0,線程進入休眠。當某個線程使用完共享資源后,釋放信號量,並將信號量內部的計數器加1,之前進入休眠的線程將被喚醒並再次試圖獲得信號量。
Semaphore除了控制資源的多個副本的並發訪問控制,也可以使用二進制信號量來實現類似synchronized關鍵字和Lock鎖的並發訪問控制功能。
37、java多線程中有哪些並發流量控制工具類?
別為CountDownLatch、CyclicBarrier、Semaphore和Exchanger,;
1、CountDownLatch,它是一種計數器的方式保證線程同步;它不去控制多個子線程之間的前后關系,只保證某一線程能夠在這些子線程執行完成后再執行。
2、CyclicBarrier,通過設置屏障的方式使得多線程同步,能夠控制多個線程在屏障處等等其他線程也執行到屏障點,可以實現CountDownLatch具有的功能,但是比CountDownLatch功能強大;
3、Semaphore,信號量,用於控制訪問某一公共資源的並發線程數;
4、Exchanger,用於兩個線程之間的數據交換。
38、如何理解動態代理?
代理模式是常用的java設計模式,他的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。簡單的說就是,我們在訪問實際對象時,是通過代理對象來訪問的,代理模式就是在訪問實際對象時引入一定程度的間接性,因為這種間接性,可以附加多種用途;
39、什么是線程安全?
如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是https://www.baidu.com/s?wd=%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao" \t "https://zhidao.baidu.com/question/_blank線程安全的。
40、能舉幾個不是線程安全的數據結構么?
Hashmap 、ArrayList 、LinkedList 、TreeMap、
41、常見的多線程數據結構有哪些,你用過其中的哪些多線程數據結構?
Vector、CopyOnWriteArrayList、CopyOnWriteArraySet、、HashTable(子類LinkedHashMap)ConcurrentHashMap
ConcurrentLinkedQueue采用的是無鎖的方式,所以其性能在高並發中很好。
BlockingQueue采用的是生產者消費者模式的方式進行加鎖。Blocking的具體實現有ArrayBlockingQueue和LinkedBlockingQueue
42、多線程的常見設計模式,你用過其中的哪些設計模式
Future模式,Master-Worker模式,生產者-消費者模型
43、什么是Master-Worker模式?如何實現Master-Worker模式?
Master-Worker模式是常用的並行計算模式。他的核心思想是系統由兩類進程協作工作:Master進程和Worker進程.Maseter負責接收和分配任務, Worker負責處理子任務。當各個Worker子進行處理完成后,會將結果返回給Master,由Msster做歸納總結,好處是能將一個大任務分解成若干個小任務,並行執行,從而提高系統的吞吐量
44、什么是Producer-Consumer模式?如何實現Producer-Consumer模式;
在生產-消費模式中:通常由兩類線程,即若干個生產者和若干個消費者的線程。生產者負責提交用戶數據,消費者負責具體處理生產者提交的任務,在生產者和消費者之間通過共享內存緩存區進行通信。
45、什么是Future模式?如何實現Future模式
Future模式類似於異步請求
46、多線程使用場景是什么?
1、數據庫的數據分析(待分析的數據太多),數據遷移。
2、servlet多線程。
3、FTP下載,多線程操作文件。
4、數據庫用到的多線程。
5、分布式計算。
6、tomcat,tomcat內部采用多線程,上百個客戶端訪問同一個WEB應用,tomcat接入后就是把后續的處理扔給一個新的線程來處理,這個新的線程最后調用我們的servlet程序,比如doGet或者dpPost方法。
7、后台任務:如定時向大量(100W以上)的用戶發送郵件;定期更新配置文件、任務調度(如quartz),一些監控用於定期信息采集。
8、自動作業處理:比如定期備份日志、定期備份數據庫。
9、異步處理:如發微博、記錄日志。
10、頁面異步處理:比如大批量數據的核對工作(有10萬個手機號碼,核對哪些是已有用戶)。
47、多線程有優缺點?
何時使用多線程技術,何時避免用它,是我們需要掌握的重要課題。多線程技術是一把雙刃劍,在使用時需要充分考慮它的優缺點。
多線程處理可以同時運行多個線程。由於多線程應用程序將程序划分成多個獨立的任務,因此可以在以下方面顯著提高性能:
(1)多線程技術使程序的響應速度更快 ,因為用戶界面可以在進行其它工作的同時一直處於活動狀態;
(2)當前沒有進行處理的任務時可以將處理器時間讓給其它任務;
(3)占用大量處理時間的任務可以定期將處理器時間讓給其它任務;
(4)可以隨時停止任務;
(5)可以分別設置各個任務的優先級以優化性能。
是否需要創建多個線程取決於各種因素。在以下情況下,最適合采用多線程處理:
(1)耗時或大量占用處理器的任務阻塞用戶界面操作;
(2)各個任務必須等待外部資源 (如遠程文件或 Internet連接)。
同樣的 ,多線程也存在許多缺點 ,在考慮多線程時需要進行充分的考慮。多線程的主要缺點包括:
(1)等候使用共享資源時造成程序的運行速度變慢。這些共享資源主要是獨占性的資源 ,如打印機等。
(2)對線程進行管理要求額外的 CPU開銷。線程的使用會給系統帶來上下文切換的額外負擔。當這種負擔超過一定程度時,多線程的特點主要表現在其缺點上,比如用獨立的線程來更新數組內每個元素。
(3)線程的死鎖。即較長時間的等待或資源競爭以及死鎖等多線程症狀。
(4)對公有變量的同時讀或寫。當多個線程需要對公有變量進行寫操作時,后一個線程往往會修改掉前一個線程存放的數據,從而使前一個線程的參數被修改;另外 ,當公用變量的讀寫操作是非原子性時,在不同的機器上,中斷時間的不確定性,會導致數據在一個線程內的操作產生錯誤,從而產生莫名其妙的錯誤,而這種錯誤是程序員無法預知的。
48、假設某系統的某個接口的峰值TPS為2w/s(其它接口的並發峰值至多為200每秒),且該接口會保存數據至數據庫,如何提升該接口的性能?
利用多線程 將並發數改成200;
創建一個任務隊列里面存入要存放任務書2.1W;創建線程2個100或1個200處理請求。如果實時,處理不來可以設置超時返回錯誤。
49、是否熟悉java concurrent包的內容,請講講concurrent包有哪些重要的內容?
locks部分:顯式鎖(互斥鎖和速寫鎖)相關;
atomic部分:原子變量類相關,是構建非阻塞算法的基礎;
executor部分:線程池相關;
collections部分:並發容器相關;
tools部分:同步工具相關,如信號量、閉鎖、柵欄等功能;
50、請講講並發編程的CAS理論
CAS 操作包含三個操作數 -- 內存位置、預期數值和新值。CAS 的實現邏輯是將內存位置處的數值與預期數值想比較,若相等,則將內存位置處的值替換為新值。若不相等,則不做任何操作。
51、請講講並發隊列和阻塞隊列
ConcurrentLinkedQueue : 是一個適用於高並發場景下的隊列,通過無鎖的方式,實現
了高並發狀態下的高性能,通常ConcurrentLinkedQueue性能好於BlockingQueue.它
是一個基於鏈接節點的無界線程安全隊列。該隊列的元素遵循先進先出的原則。頭是最先
加入的,尾是最近加入的,該隊列不允許null元素。
阻塞隊列(BlockingQueue)是一個支持兩個附加操作的隊列。
阻塞隊列常用於生產者和消費者的場景,生產者是往隊列里添加元素的線程,消費者是從隊列里拿元素的線程。阻塞隊列就是生產者存放元素的容器,而消費者也只從容器里拿元素。
BlockingQueue即阻塞隊列,從阻塞這個詞可以看出,在某些情況下對阻塞隊列的訪問可能會造成阻塞。被阻塞的情況主要有如下兩種:1. 當隊列滿了的時候進行入隊列操作2. 當隊列空了的時候進行出隊列操作
因此,當一個線程試圖對一個已經滿了的隊列進行入隊列操作時,它將會被阻塞,除非有另一個線程做了出隊列操作;同樣,當一個線程試圖對一個空隊列進行出隊列操作時,
它將會被阻塞,除非有另一個線程進行了入隊列操作。
在Java中,BlockingQueue的接口位於java.util.concurrent 包中(在Java5版本開始提供),由上面介紹的阻塞隊列的特性可知,阻塞隊列是線程安全的。
在新增的Concurrent包中,BlockingQueue很好的解決了多線程中,如何高效安全“傳輸”數據的問題。通過這些高效並且線程安全的隊列類。
52、多線程yield方法使用於什么場景?
Thread.yield()方法作用是:暫停當前正在執行的線程對象(及放棄當前擁有的cup資源),並執行其他線程。
yield()做的是讓當前運行線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行機會。因此,使用yield()的
目的是讓相同優先級的線程之間能適當的輪轉執行。但是,實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被
線程調度程序再次選中。
53、請講講線程異步處理的原理及關鍵組件?
在http://lib.csdn.net/base/javase" \o "Java SE知識庫" \t "https://www.cnblogs.com/tuojunjie/p/_blankJava平台,實現異步調用的角色有如下三個角色:調用者、 提貨單 、真實數據,一個調用者在調用耗時操作,不能立即返回數據時,先返回一個提貨單
.然后在過一斷時間后憑提貨單來獲取真正的數據.去蛋糕店買蛋糕,不需要等蛋糕做出來(假設現做要很長時間),只需要領個提貨單就可以了(去干別的
事情),等到蛋糕做好了,再拿提貨單取蛋糕就可以了。
54、在實際項目(產品)研發過程中,你是否有使用過多線程,和線程池,如果有,請舉例說明(要用STAR模型);
55、什么是多線程的原子操作?Java 中有哪些原子操作?
即不能被線程調度機制中斷的操作。原子操作不需要進行同步控制。
原子操作可以是一個步驟,也可以是多個操作步驟,但是其順序不可以被打亂,也不可以被切割而只執行其中的一部分,將整個操作視作一個整體是原子性的核心特征;
原子更新基本類型
1)除long和double之外的基本類型的賦值操作
2)所有引用reference的賦值操作
3)java.concurrent.Atomic.* 包中所有類的一切操作
AtomicBoolean :原子更新布爾類型
AtomicInteger: 原子更新整型
AtomicLong: 原子更新長整型
原子更新數組
AtomicIntegerArray :原子更新整型數組里的元素
AtomicLongArray :原子更新長整型數組里的元素
AtomicReferenceArray : 原子更新引用類型數組的元素
AtomicBooleanArray :原子更新布爾類型數組的元素
原子更新引用類型
AtomicReference :原子更新引用類型
AtomicReferenceFieldUpdater :原子更新引用類型里的字段
AtomicMarkableReference:原子更新帶有標記位的引用類型。可以原子更新一個布爾類型的標記位和應用類型
原子更新字段類
AtomicIntegerFieldUpdater:原子更新整型的字段的更新器
AtomicLongFieldUpdater:原子更新長整型字段的更新器
AtomicStampedReference:原子更新帶有版本號的引用類型。該類將整型數值與引用關聯起來,可用於原子的更新數據和數據的版本號,可以解決使用CAS進行原子更新時可能出現的ABA問題。
56、多線程的原子操作類的使用場景是什么,你在項目的實際研發過程中是否有使用過原子操作類?
原子操作可以是一個步驟,也可以是多個操作步驟,但是其順序不可以被打亂,也不可以被切割而只執行其中的一部分,將整個操作視作一個整體是原子性的核心特征;
原子更新基本類型
計數器 可以用原子操作
57、如何在多個線程間共享數據?
如果每個線程執行的代碼相同,可以使用同一個Runnable對象,這個Runnable對象中有那個共享數據,例如,賣票系統就可以這么做。
將共享數據封裝成另外一個對象,然后將這個對象逐一傳遞給各個Runnable對象,每個線程對共享數據的操作方法也分配到那個對象身上完成,這樣容易實現針對數據進行各個操作的互斥和通信
將Runnable對象作為一個類的內部類,共享數據作為這個類的成員變量,每個線程對共享數據的操作方法也封裝在外部類,以便實現對數據的各個操作的同步和互斥,作為內部類的各個Runnable對象調用外部類的這些方法。
58、線程的狀態有哪些,線程狀態的使用場景是什么?
1、新狀態:線程對象已經創建,還沒有在其上調用start()方法。
2、可運行狀態:當線程有資格運行,但調度程序還沒有把它選定為運行線程時線程所處的狀態。當start()方法調用時,線程首先進入可運行狀態。在線程運行之后或者從阻塞、等待或睡眠狀態回來后,也返回到可運行狀態。
3、運行狀態:線程調度程序從可運行池中選擇一個線程作為當前線程時線程所處的狀態。這也是線程進入運行狀態的唯一一種方式。
4、等待/阻塞/睡眠狀態:這是線程有資格運行時它所處的狀態。實際上這個三狀態組合為一種,其共同點是:線程仍舊是活的,但是當前沒有條件運行。換句話說,它是可運行的,但是如果某件事件出現,他可能返回到可運行狀態。
5、死亡態:當線程的run()方法完成時就認為它死去。這個線程對象也許是活的,但是,它已經不是一個單獨執行的線程。線程一旦死亡,就不能復生。如果在一個死去的線程上調用start()方法,會拋出java.lang.IllegalThreadStateException異常。
59、有多個線程T1,T2,T3,怎么確保它們按順序執行?
1)可以在線程里面加入join方法 一次等待前面的線程執行完 在執行后面的
2)用Executors.newSingleThreadExecutor();分別依次提交 開啟,這樣就執行有序了。
60、volatile變量和atomic變量有什么不同?
Volatile是讓變量在所有線程中變得可見。操作時不一定保證原子性,線程安全。
Atomic是原子性,線程安全的。他的修改是sysnizied。
61、wait/notify/notifyAll一般使用於什么場景?
如果線程調用了對象的wait()方法,那么線程便會處於該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
當有線程調用了對象的notifyAll()方法(喚醒所有wait線程)或notify()方法(只隨機喚醒一個wait線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。
優先級高的線程競爭到對象鎖的概率大,假若某線程沒有競爭到該對象鎖,它還會留在鎖池中,唯有線程再次調用wait()方法,它才會重新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了synchronized代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。
62、什么是JVM ?
JVM是Java Virtual Machine(Javahttps://baike.baidu.com/item/%E8%99%9A%E6%8B%9F%E6%9C%BA" \t "https://baike.baidu.com/item/JVM/_blank虛擬機)的縮寫,JVM是一種用於計算設備的規范,它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的。
https://baike.baidu.com/item/Java%E8%AF%AD%E8%A8%80" \t "https://baike.baidu.com/item/JVM/_blankJava語言的一個非常重要的特點就是與平台的無關性。而使用Java虛擬機是實現這一特點的關鍵。一般的高級語言如果要在不同的平台上運行,至少需要編譯成不同的https://baike.baidu.com/item/%E7%9B%AE%E6%A0%87%E4%BB%A3%E7%A0%81/9407934" \t "https://baike.baidu.com/item/JVM/_blank目標代碼。而引入Java語言虛擬機后,Java語言在不同平台上運行時不需要重新編譯。Java語言使用Java虛擬機屏蔽了與具體平台相關的信息,使得Java語言https://baike.baidu.com/item/%E7%BC%96%E8%AF%91%E7%A8%8B%E5%BA%8F/8290180" \t "https://baike.baidu.com/item/JVM/_blank編譯程序只需生成在Java虛擬機上運行的目標代碼(https://baike.baidu.com/item/%E5%AD%97%E8%8A%82%E7%A0%81/9953683" \t "https://baike.baidu.com/item/JVM/_blank字節碼),就可以在多種平台上不加修改地運行。Java虛擬機在執行字節碼時,把字節碼解釋成具體平台上的https://baike.baidu.com/item/%E6%9C%BA%E5%99%A8%E6%8C%87%E4%BB%A4/8553126" \t "https://baike.baidu.com/item/JVM/_blank機器指令執行。這就是Java的能夠“一次編譯,到處運行”的原因。
63、Java中堆和棧有什么區別?
各司其職:
最主要的區別就是棧內存用來存儲局部變量和方法調用。
而堆內存用來存儲Java中的對象。無論是成員變量,局部變量,還是類變量,它們指向的對象都存儲在堆內存中。
獨有還是共享:
棧內存歸屬於單個線程,每個線程都會有一個棧內存,其存儲的變量只能在其所屬線程中可見,即棧內存可以理解成線程的私有內存。
而堆內存中的對象對所有線程可見。堆內存中的對象可以被所有線程訪問。
異常錯誤:
如果棧內存沒有可用的空間存儲方法調用和局部變量,JVM會拋出java.lang.StackOverFlowError。
而如果是堆內存沒有可用的空間存儲生成的對象,JVM會拋出java.lang.OutOfMemoryError。
空間大小:
棧的內存要遠遠小於堆內存,如果你使用遞歸的話,那么你的棧很快就會充滿。如果遞歸沒有及時跳出,很可能發生StackOverFlowError問題。
你可以通過-Xss選項設置棧內存的大小。-Xms選項可以設置堆的開始時的大小,-Xmx選項可以設置堆的最大值。
這就是Java中堆和棧的區別。理解好這個問題的話,可以對你解決開發中的問題,分析堆內存和棧內存使用,甚至性能調優都有幫助。
64、請說說jvm的基本結構?
它包括:類加載器子系統、運行時數據區、執行引擎和本地方法接口。
運行時數據區是JVM從操作系統申請來的堆空間和操作系統給JVM分配的棧空間的總稱。JVM為了運行Java程序,又進一步對運行時數據區進行了划分,划分為Java方法區、Java堆、Java棧、PC寄存器、本地方法棧等,這里JVM從操作系統申請來的堆空間被划分為方法區和Java堆,操作系統給JVM分配的棧空間構成Java棧。
65、堆空間的結構
運行時數據區中Java的方法區和Java堆(圖中顯示的是:永久、新生、老年,這是分代垃圾回收時的術語,實際上永久代和Java方法區對應,https://www.baidu.com/s?wd=%E6%96%B0%E7%94%9F%E4%BB%A3&tn=24004469_oem_dg&rsv_dl=gh_pl_sl_csd" \t "https://blog.csdn.net/zhangzl4321/article/details/_blank新生代和老年代和Java堆對應),也就說Java方法區和Java堆其實都是JVM堆的一部分。JVM的棧區構成了Java的線程棧。
66、為何新生代要設置兩個survivor區,jvm的設計上有何目的?
1、Survivor的存在意義,就是減少被送到老年代的對象,進而減少Full GC的發生,Survivor的預篩選保證,只有經歷16次Minor GC還能在新生代中存活的對象,才會被送到老年代。
2、設置兩個Survivor區最大的好處就是解決了碎片化,永遠有一個survivor space是空的,另一個非空的survivor space無碎片。
設計目的:
應該建立兩塊Survivor區,剛剛新建的對象在Eden中,經歷一次Minor GC,Eden中的存活對象就會被移動到第一塊survivor space S0,Eden被清空;等Eden區再滿了,就再觸發一次Minor GC,Eden和S0中的存活對象又會被復制送入第二塊survivor space S1(這個過程非常重要,因為這種復制算法保證了S1中來自S0和Eden兩部分的存活對象占用連續的內存空間,避免了碎片化的發生)。S0和Eden被清空,然后下一輪S0與S1交換角色,如此循環往復。如果對象的復制次數達到16次,該對象就會被送到老年代中。下圖中每部分的意義和上一張圖一樣,就不加注釋了。
67、垃圾回收中的復制算法適用於在什么場景下使用?
將內存分為(大小相等)兩部分,每次只使用其中一塊進行內存分配,當內存使用完后,就出發GC,將存活的對象直接復制到另一塊空閑的內存中,然后對當前使用的內存塊一次性清除所有,然后轉到另一塊內存進行使用。
優點:簡單,高效。
缺點:浪費內存,因為每次都有另一塊內存空閑着。
Eden survivor 垃圾回收
68、老年代的垃圾回收一般用什么算法?
標記-壓縮-清理算法進行垃圾回收,將標記對象移動到堆的另一端,同時更新對象的引用地址
1、mark_sweep_phase1: 標記活躍對象
2、mark_sweep_phase2: 計算活躍對象在壓縮完成之后的新地址
69、怎么獲取 Java 程序使用的內存?堆使用的百分比?
jhat:內存分析工具:
主要是對java應用程序的資源和性能進行實時的命令行監控,包括了對heap size和垃圾回收狀況的監控。
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
option:我們經常使用的選項有gc、gcutil
vmid:java進程id
interval:間隔時間,單位為毫秒
count:打印次數
jmap -heap pid:查看堆使用情況
jmap -histo pid:查看堆中對象數量和大小
jmap -dump:format=b,file=heapdump pid:將內存使用的詳細情況輸出到文件
序列號、Class實例的數量、內存的占用、類限定名
如果是內部類,類名的開頭會加上*,如果加上live子參數的話,如jmap -histo:live pid,這個命名會觸發一次FUll GC,只統計存活對象
70、GC回收機制?
71、jmap命令是有什么用途?jstat命令是有什么用途?
https://www.baidu.com/s?wd=Jmap&tn=24004469_oem_dg&rsv_dl=gh_pl_sl_csd" \t "_blankmap是一個可以輸出所有內存中對象的工具,甚至可以將VM 中的heap,以二進制輸出成文本。打印出某個java進程(使用pid)內存內的,
Jstat是JDK自帶的一個輕量級小工具。全稱“Java Virtual Machine statistics monitoring tool”,它位於java的bin目錄下,主要利用JVM內建的指令對Java應用程序的資源和性能進行實時的命令行的監控,包括了對Heap size和垃圾回收狀況的監控。可見,Jstat是輕量級的、專門針對JVM的工具,非常適用。
72、有哪些常見的jvm命令,說說各自的用途是什么?
Jps:JVM Process Status Tool,顯示指定系統內所有的HotSpot虛擬機進程
Jstat: 是用於監視虛擬機運行時狀態信息的命令,它可以顯示出虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據。
Jmap:(JVM Memory Map)命令用於生成heap dump文件,如果不使用這個命令,還闊以使用-XX:+HeapDumpOnOutOfMemoryError參數來讓虛擬機出現OOM的時候·自動生成dump文件。
jmap不僅能生成dump文件,還闊以查詢finalize執行隊列、Java堆和永久代的詳細信息,如當前使用率、當前使用的是哪種收集器等。
jhat(JVM Heap Analysis Tool)命令是與jmap搭配使用,用來分析jmap生成的dump,jhat內置了一個微型的HTTP/HTML服務器,生成dump的分析結果后,可以在瀏覽器中查看。在此要注意,一般不會直接在服務器上進行分析,因為jhat是一個耗時並且耗費硬件資源的過程,一般把服務器生成的dump文件復制到本地或其他機器上進行分析。
jstack用於生成java虛擬機當前時刻的線程快照。線程快照是當前java虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導致的長時間等待等。 線程出現停頓的時候通過jstack來查看各個線程的調用堆棧,就可以知道沒有響應的線程到底在后台做什么事情,或者等待什么資源。 如果java程序崩潰生成core文件,jstack工具可以用來獲得core文件的java stack和native stack的信息,從而可以輕松地知道java程序是如何崩潰和在程序何處發生問題。另外,jstack工具還可以附屬到正在運行的java程序中,看到當時運行的java程序的java stack和native stack的信息, 如果現在運行的java程序呈現hung的狀態,jstack是非常有用的。
info(JVM Configuration info)這個命令作用是實時查看和調整虛擬機運行參數。
之前的jps -v口令只能查看到顯示指定的參數,如果想要查看未被顯示指定的參數的值就要使用jinfo口令
73、、GC有哪些算法(*****)
引用計數,沒有被Java采用
標記-清除
標記-壓縮 標記-整理算法
復制算法 新生代
74、、什么是線程中斷。tips: stop the world,簡稱STW,參考billy1.GC算法與種類
Java中一種全局暫停的現象
全局停頓,所有Java代碼停止,native代碼可以執行,但不能和JVM交互
75、MGC、FGC分別是什么意思,它們在什么情況下會發生
,YG用來放新產生的對象,經過幾次回收還沒回收掉的對象往OG中移動,對YG進行垃圾回收又叫做MinorGC,對 OG垃圾回收又叫MajorGC,.
1、當eden滿了,觸發young GC;
2.young GC做2件事:一,去掉一部分沒用的object;二,把老的還被引用的object發到survior里面,等下幾次GC以后,survivor再放到old里面。
3.當old滿了,觸發full GC。full GC很消耗內存,把old,young里面大部分垃圾回收掉。這個時候用戶線程都會被block。
76、、請講講jvm的分代,為什么要分代,jvm分代有什么好處?
虛擬機中的共划分為三個代:年輕代(Young Generation)、年老點(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java類的類信息,與垃圾收集要收集的Java對象關系不大。年輕代和年老代的划分是對垃圾收集影響比較大的。
利用對象存活的生命不同。利用的算法不同。
所有新生成的對象首先都是放在年輕代的。年輕代的目標就是盡可能快速的收集掉那些生命周期短的對象。年輕代分三個區。一個Eden區,兩個Survivor區(一般而言)。大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被復制到Survivor區(兩個中的一個),當這個Survivor區滿時,此區的存活對象將被復制到另外一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區復制過來的並且此時還存活的對象,將被復制“年老區(Tenured)”。需要注意,Survivor的兩個區是對稱的,沒先后關系,所以同一個區中可能同時存在從Eden復制過來 對象,和從前一個Survivor復制過來的對象,而復制到年老區的只有從第一個Survivor去過來的對象。而且,Survivor區總有一個是空的。同時,根據程序需要,Survivor區是可以配置為多個的(多於兩個),這樣可以增加對象在年輕代中的存在時間,減少被放到年老代的可能。
77、、你知道哪些jvm調優工具么?
uptime 系統時間 運行時間 連接數 1,5,15分鍾內的系統平均負載
top
vmstat可以統計系統的CPU,內存,swap,io等情況
pidstat細致觀察進程
任務管理器
Perfmon
Process Explorer
pslist
78、、在jvm中,年輕代如何向老年代轉變的?年輕代向老年代轉換的重要參數是什么?
當eden滿了,觸發young GC;
2.young GC做2件事:一,去掉一部分沒用的object;二,把老的還被引用的object發到survior里面,等下幾次GC以后,survivor再放到old里面。
3.當old滿了,觸發full GC。full GC很消耗內存,把old,young里面大部分垃圾回收掉。這個時候用戶線程都會被block。
-Xms 和 -Xmx (-XX:InitialHeapSize 和 -XX:MaxHeapSize):指定JVM初始占用的堆內存和最大堆內存。JVM也是一個軟件,也必須要獲取本機的物理內
-XX:MaxTenuringThreshold= 設置熬過年輕代多少次收集后移入老人區,CMS中默認為0,熬過第一次GC就轉入,可以用-XX:+PrintTenuringDistribution 查看
調用16次
79、、直接內存使用場景是什么,使用直接內存可能會存在什么問題?tips
80、、堆內存有哪些重要參數?
默認空余堆內存小於40%時,JVM就會增大堆直到-Xmx的最大限制,可以由 -XX:MinHeapFreeRatio=指定。
默認空余堆內存大於70%時,JVM會減少堆直到-Xms的最小限制,可以由 -XX:MaxHeapFreeRatio=指定。
服務器一般設置-Xms、-Xmx相等以避免在每次GC后調整堆的大小,所以上面的兩個參數沒啥用。
oung(Nursery):年輕代
研究表明大部分對象都是朝生暮死,隨生隨滅的。所以對於年輕代在GC時都采取復制收集算法,具體算法參考下面的描述;
Young的默認值為4M,隨堆內存增大,約為1/15,JVM會根據情況動態管理其大小變化。
Young里面又分為3 個區域,一個Eden,所有新建對象都會存在於該區,兩個Survivor區,用來實施復制算法。
-XX:NewRatio= 參數可以設置Young與Old的大小比例,-server時默認為1:2,但實際上young啟動時遠低於這個比率?如果信不過JVM,也可以用 -Xmn硬性規定其大小,有文檔推薦設為Heap總大小的1/4。
-XX:SurvivorRatio= 參數可以設置Eden與Survivor的比例,默認為32。Survivio大了會浪費,小了的話,會使一些年輕對象潛逃到老人區,引起老人區的不安,但這個參數對性能並不太重要。
Old(Tenured):年老代
年輕代的對象如果能夠挺過數次收集,就會進入老人區。老人區使用標記整理算法。因為老人區的對象都沒那么容易死的,采用復制算法就要反復的復制對象,很不合算,只好采用標記清理算法,但標記清理算法其實也不輕松,每次都要遍歷區域內所有對象,所以還是沒有免費的午餐啊。
-XX:MaxTenuringThreshold= 設置熬過年輕代多少次收集后移入老人區,CMS中默認為0,熬過第一次GC就轉入,可以用-XX:+PrintTenuringDistribution 查看。
Permanent:持久代
裝載Class信息等基礎數據,默認64M,如果是類很多很多的服務程序,需要加大其設置 -XX:MaxPermSize=,否則它滿了之后會引起fullgc()或Out of Memory。 注意Spring,Hibernate這類喜歡AOP動態生成類的框架需要更多的持久代內存。一般情況下,持久代是不會進行GC的,除非通過 -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled進行強制設置。
81、如何設置堆大小,是否有一些經驗值?
JVM 中最大堆大小有三方面限制:相關操作系統的數據模型(32-bt還是64-bit)限制;系統的可用虛擬內存限制;系統的可用物理內存限制。32位系統 下,一般限制在1.5G~2G;64為操作系統對內存無限制。我在Windows Server 2003 系統,3.5G物理內存,JDK5.0下測試,最大可設置為1478m。
典型JVM參數設置:
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-Xmx3550m:設置JVM最大可用內存為3550M。
-Xms3550m:設置JVM促使內存為3550m。此值可以設置與-Xmx相同,以避免每次垃圾回收完成后JVM重新分配內存。
-Xmn2g:設置年輕代大小為2G。整個堆大小=年輕代大小 + 年老代大小 + 持久代大小。持久代一般固定大小為64m,所以增大年輕代后,將會減小年老代大小。此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8。
-Xss128k:設置每個線程的堆棧大小。JDK5.0以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256K。更具應用的線程所需內存大小進行 調整。在相同物理內存下,減小這個值能生成更多的線程。但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在 3000~5000 左右。
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4:設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設置為4,則年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5
-XX:SurvivorRatio=4:設置年輕代中Eden區與Survivor區的大小比值。設置為4,則兩個Survivor區與一個 Eden區的比值為2:4,一個Survivor區占整個年輕代的1/6
-XX:MaxPermSize=16m:設置持久代大小為16m。
-XX:MaxTenuringThreshold=0:設置垃圾最大年齡。如果設置為0的話,則年輕代對象不經過Survivor區,直接進入年老代。 對於年老代比較多的應用,可以提高效率。如果將此值設置為一個較大值,則年輕代對象會在Survivor區進行多次復制,這樣可以增加對象再年輕代的存活 時間,增加在年輕代即被回收的概論。
82、如何打印JVM日志?
-XX:+PrintGCDetails -Xloggc:../logs/gc.log -XX:+PrintGCTimeStamps
83、請介紹常見的jvm參數
-XX:+PrintGCTimeStamps:
打印此次垃圾回收距離jvm開始運行的所耗時間
-XX:+PrintGCDeatils
打印垃圾回收的細節信息
-Xloggc:<filename>
將垃圾回收信息輸出到指定文件
-XX:+PrintGCDateStamps
需要打印日歷形式的時間戳選項
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCApplicationConcurrentTime
打印應用程序由於執行VM安全點操作而阻塞的時間以及兩個安全點操作之間應用程序的運行時間
-XX:+PrintSafepointStatistics
可以將垃圾回收的安全點與其他的安全點區分開
-XX:+UseSerialGC:在新生代和老年代使用串行收集器
-XX:SurvivorRatio:設置eden區大小和survivior區大小的比例
-XX:NewRatio:新生代和老年代的比
-XX:+UseParNewGC:在新生代使用並行收集器
-XX:+UseParallelGC :新生代使用並行回收收集器
-XX:+UseParallelOldGC:老年代使用並行回收收集器
-XX:ParallelGCThreads:設置用於垃圾回收的線程數
-XX:+UseConcMarkSweepGC:新生代使用並行收集器,老年代使用CMS+串行收集器
-XX:ParallelCMSThreads:設定CMS的線程數量
-XX:CMSInitiatingOccupancyFraction:設置CMS收集器在老年代空間被使用多少后觸發
-XX:+UseCMSCompactAtFullCollection:設置CMS收集器在完成垃圾收集后是否要進行一次內存碎片的整理
-XX:CMSFullGCsBeforeCompaction:設定進行多少次CMS垃圾回收后,進行一次內存壓縮
-XX:+CMSClassUnloadingEnabled:允許對類元數據進行回收
-XX:CMSInitiatingPermOccupancyFraction:當永久區占用率達到這一百分比時,啟動CMS回收
-XX:UseCMSInitiatingOccupancyOnly:表示只在到達閥值的時候,才進行CMS回收
84、CMS收集器有什么特點?
盡可能降低停頓
會影響系統整體吞吐量和性能
比如,在用戶線程運行過程中,分一半CPU去做GC,系統性能在GC階段,反應速度就下降一半
清理不徹底
因為在清理階段,用戶線程還在運行,會產生新的垃圾,無法清理
因為和用戶線程一起運行,不能在空間快滿時再清理
85、G1收集器有什么特點?
並行於並發:G1能充分利用CPU、多核環境下的硬件優勢,使用多個CPU(CPU或者CPU核心)來縮短stop-The-World停頓時間。部分其他收集器原本需要停頓Java線程執行的GC動作,G1收集器仍然可以通過並發的方式讓java程序繼續執行。
2、分代收集:雖然G1可以不需要其他收集器配合就能獨立管理整個GC堆,但是還是保留了分代的概念。它能夠采用不同的方式去處理新創建的對象和已經存活了一段時間,熬過多次GC的舊對象以獲取更好的收集效果。
3、空間整合:與CMS的“標記--清理”算法不同,G1從整體來看是基於“標記整理”算法實現的收集器;從局部上來看是基於“復制”算法實現的。
4、可預測的停頓:這是G1相對於CMS的另一個大優勢,降低停頓時間是G1和CMS共同的關注點,但G1除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明確
86、垃圾回收器有哪些?
串行垃圾回收器(Serial Garbage Collector)
並行垃圾回收器(Parallel Garbage Collector)
並發標記掃描垃圾回收器(CMS Garbage Collector)
G1垃圾回收器(G1 Garbage Collector)
87、java內存模型
1、主內存和工作內存
(1)所有變量均存儲在主內存(虛擬機內存的一部分)
(2)每個線程都對應着一個工作線程,主內存中的變量都會復制一份到每個線程的自己的工作空間,線程對變量的操作都在自己的工作內存中,操作完成后再將變量更新至主內存;
(3)其他線程再通過主內存來獲取更新后的變量信息,即線程之間的交流通過主內存來傳遞
Note:JMM的空間划分和JVM的內存划分不一樣,非要對應的話,關系如下:
(1)JMM的主內存對應JVM中的堆內存對象實例數據部分
(2)JMM的工作內存對應JVM中棧中部分區域
88、什么是類加載器,類加載器有哪些,類加載器的加載順序是什么?
類加載器是一個用來加載類文件的類。Java源代碼通過javac編譯器編譯成類文件。然后JVM來執行類文件中的字節碼來執行程序。類加載器負責加載文件系統、網絡或其他來源的類文件。有三種默認使用的類加載器:Bootstrap類加載器、Extension類加載器和System類加載器(或者叫作Application類加載器)。
1)Bootstrap類加載器 – JRE/lib/rt.jar
2) Extension類加載器 – JRE/lib/ext或者java.ext.dirs指向的目錄
3) Application類加載器 – CLASSPATH環境變量, 由-classpath或-cp選項定義,或者是JAR中的Manifest的classpath屬性定義.
VM並不是把所有的類一次性全部加載到JVM中的,也不是每次用到一個類的時候都去查找,對於JVM級別的類加載器在啟動時就會把默認的JAVA_HOME/lib里的class文件加載到JVM中,因為這些是系統常用的類,對於其他的第三方類,則采用用到時就去找,找到了就緩存起來的,下次再用到這個類的時候就可以直接用緩存起來的類對象了,ClassLoader之間也是有父子關系的,沒個ClassLoader都有一個父ClassLoader,在加載類時ClassLoader與其父ClassLoader的查找
89、簡述java內存分配與回收策略
1、 當eden滿了,觸發young GC;
2.young GC做2件事:一,去掉一部分沒用的object;二,把老的還被引用的object發到survior里面,等下幾次GC以后,survivor再放到old里面。
3.當old滿了,觸發full GC。full GC很消耗內存,把old,young里面大部分垃圾回收掉。這個時候用戶線程都會被block。
•Tips:eden、Sruvivor、老年代、永久代(元空間)
90、JDK1.8之后Perm Space有哪些變動? MetaSpace大小默認是無限的么? 還是你們會通過什么方式來指定大小?
1、 JDK 1.8后用元空間替代了 Perm Space;字符串常量存放到堆內存中。
2、 MetaSpace大小默認沒有限制,一般根據系統內存的大小。JVM會動態改變此值。
3、 -XX:MetaspaceSize:分配給類元數據空間(以字節計)的初始大小(Oracle邏輯存儲上的初始高水位,the initial high-water-mark)。此值為估計值,MetaspaceSize的值設置的過大會延長垃圾回收時間。垃圾回收過后,引起下一次垃圾回收的類元數據空間的大小可能會變大。
4、 -XX:MaxMetaspaceSize:分配給類元數據空間的最大值,超過此值就會觸發Full GC,此值默認沒有限制,但應取決於系統內存的大小。JVM會動態地改變此值。
91、Perm Space中保存什么數據?會引起OutOfMemory嗎?
加載class文件。
會引起,出現異常可以設置 -XX:PermSize 的大小。JDK 1.8后,字符串常量不存放在永久帶,而是在堆內存中,JDK8以后沒有永久代概念,而是用元空間替代,元空間不存在虛擬機中,二是使用本地內存。
詳細查看Java8內存模型—永久代(PermGen)和元空間(Metaspace)
92、java類加載全過程,從架構角度理解,類加載和反射、動態代理有什么關系?
93、簡述java類加載機制?tips:看ClassLoader源碼講解類加載機制,理解記憶
加載----驗證----准備----解析-----初始化----使用-----卸載
94、GC收集器有哪些?CMS收集器與G1收集器的特點
串行垃圾回收器(Serial Garbage Collector)
並行垃圾回收器(Parallel Garbage Collector)
並發標記掃描垃圾回收器(CMS Garbage Collector)
G1垃圾回收器(G1 Garbage Collector)
95、類加載器雙親委派模型機制,“雙親委派”中的雙親是什么意思?tips:演示ClassLoaderTest2,講解雙親委派流程圖
如果一個類加載器收到類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器完成。每個類加載器都是如此,只有當父加載器在自己的搜索范圍內找不到指定的類時(即ClassNotFoundException),子加載器才會嘗試自己去加載。
96、什么情況下會出現永久代內存溢出,如何解決此類問題?
生成大量的類,增大Perm區 允許Class回收
97、什么情況下會出現堆內存溢出,如何解決此類問題?
占用大量堆空間,直接溢出
增大堆空間,及時釋放內存
98、什么情況下會出現直接內存溢出,如何解決此類問題?
ByteBuffer.allocateDirect()無法從操作系統獲得足夠的空間
解決方法:減少堆內存 有意觸發GC
99、什么情況下會出現過多線程導致內存溢出的問題,如何解決此類問題?
多線程 沒有釋放內存
– 1、OOM由於保存多線程過多引起,可以考慮增加堆大小
– 2. 如果應用允許,縮短多線程的過期時間,使得session可以及時過期,並回收
100、什么情況下會出現CPU使用率過高的問題,如何解決此類問題?
多線程競爭資源,多線程上下文切換太頻繁
合理設置線程最大開啟數量,並發數量