目錄介紹
- 5.0.0.1 線程池具備什麼優勢和缺點?爲何說開啓大量的線程,會下降程序的性能,那麼該如何作才能下降性能?
- 5.0.0.3 線程中start和run方法有什麼區別?wait和sleep方法的不一樣?sleep() 、join()、yield()有什麼區別?
- 5.0.0.4 用Java手寫一個會致使死鎖的程序,遇到這種問題解決方案是什麼?那些場景用到了死鎖機制?
- 5.0.0.5 ThreadLocal(線程變量副本)這個類的做用是什麼?
- 5.0.0.6 什麼是線程安全?線程安全有那幾個級別?保障線程安全有哪些手段?ReentrantLock和synchronized的區別?
- 5.0.0.7 Volatile和Synchronized各自用途是什麼?有哪些不一樣點?Synchronize在編譯時如何實現鎖機制?
- 5.0.0.8 wait()和sleep()的區別?各自有哪些使用場景?怎麼喚醒一個阻塞的線程?Thread.sleep(0)的做用是啥?
- 5.0.0.9 同步和非同步、阻塞和非阻塞的概念?分別有哪些使用場景?
- 5.0.1.0 線程的有哪些狀態?請繪製該狀態的流程圖?講一下線程的執行生命週期流程?線程若是出現了運行時異常會怎麼樣?
- 5.0.1.1 synchronized鎖什麼?synchronized同步代碼塊還有同步方法本質上鎖住的是誰?爲何?
- 5.0.1.2 Volatile實現原理?一個int變量,用volatile修飾,多線程去操做++,線程安全嗎?那如何才能保證i++線程安全?
- 5.0.1.3 CAS原理是什麼?CAS實現原子操做會出現什麼問題?
- 5.0.1.4 假若有n個網絡線程,須要當n個網絡線程完成以後,再去作數據處理,你會怎麼解決?
- 5.0.1.5 Runnable接口和Callable接口的區別?
- 5.0.1.6 若是提交任務時,線程池隊列已滿,這時會發生什麼?線程調度算法是什麼?
- 5.0.1.7 什麼是樂觀鎖和悲觀鎖?
- 5.0.1.8 線程類的構造方法、靜態塊是被哪一個線程調用的?同步方法和同步塊,哪一個是更好的選擇?同步的範圍越少越好嗎?
- 5.0.1.9 synchonized(this)和synchonized(object)區別?Synchronize做用於方法和靜態方法區別?
好消息
- 博客筆記大彙總【15年10月到至今】,包括Java基礎及深刻知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug彙總,固然也在工做之餘收集了大量的面試題,長期更新維護而且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計500篇[近100萬字],將會陸續發表到網上,轉載請註明出處,謝謝!
- 連接地址:https://github.com/yangchong2...
- 若是以爲好,能夠star一下,謝謝!固然也歡迎提出建議,萬事起於忽微,量變引發質變!全部博客將陸續開源到GitHub!
5.0.0.1 線程池具備什麼優勢和缺點?爲何說開啓大量的線程,會下降程序的性能,那麼該如何作才能下降性能?
-
線程池好處:php
- 1)下降資源消耗;
- 2)提升相應速度;
- 3)提升線程的可管理性。技術博客大總結
-
線程池的實現原理:html
- 當提交一個新任務到線程池時,判斷核心線程池裏的線程是否都在執行。若是不是,則建立一個新的線程執行任務。若是核心線程池的線程都在執行任務,則進入下個流程。
- 判斷工做隊列是否已滿。若是未滿,則將新提交的任務存儲在這個工做隊列裏。若是工做隊列滿了,則進入下個流程。
- 判斷線程池是否都處於工做狀態。若是沒有,則建立一個新的工做線程來執行任務。若是滿了,則交給飽和策略來處理這個任務。
5.0.0.3 線程中start和run方法有什麼區別?wait和sleep方法的不一樣?sleep() 、join()、yield()有什麼區別?
5.0.0.4 用Java手寫一個會致使死鎖的程序,遇到這種問題解決方案是什麼?那些場景用到了死鎖機制?
-
死鎖是怎麼一回事緩存
- 線程A和線程B相互等待對方持有的鎖致使程序無限死循環下去。
-
深刻理解死鎖的原理安全
- 兩個線程裏面分別持有兩個Object對象:lock1和lock2。這兩個lock做爲同步代碼塊的鎖;
- 線程1的run()方法中同步代碼塊先獲取lock1的對象鎖,Thread.sleep(xxx),時間不須要太多,50毫秒差很少了,而後接着獲取lock2的對象鎖。這麼作主要是爲了防止線程1啓動一會兒就連續得到了lock1和lock2兩個對象的對象鎖
- 線程2的run)(方法中同步代碼塊先獲取lock2的對象鎖,接着獲取lock1的對象鎖,固然這時lock1的對象鎖已經被線程1鎖持有,線程2確定是要等待線程1釋放lock1的對象鎖的
5.0.0.5 ThreadLocal(線程變量副本)這個類的做用是什麼?
-
ThreadLocal即線程變量
- ThreadLocal爲每一個線程維護一個本地變量。
- 採用空間換時間,它用於線程間的數據隔離,它爲每一個使用該變量的線程提供獨立的變量副本,因此每個線程均可以獨立地改變本身的副本,而不會影響其它線程所對應的副本。從線程的角度看,目標變量就象是線程的本地變量,這也是類名中「Local」所要表達的意思。ThreadLocal的實現是以ThreadLocal對象爲鍵。任意對象爲值得存儲結構。這個結構被附帶在線程上,也就是說一個線程能夠根據一個ThreadLocal對象查詢到綁定在這個線程上的一個值。
-
ThreadLocal類是一個Map
- ThreadLocal類中維護一個Map,用於存儲每個線程的變量副本,Map中元素的鍵爲線程對象,而值爲對應線程的變量副本。
- ThreadLocal在Spring中發揮着巨大的做用,在管理Request做用域中的Bean、事務管理、任務調度、AOP等模塊都出現了它的身影。
- Spring中絕大部分Bean均可以聲明成Singleton做用域,採用ThreadLocal進行封裝,所以有狀態的Bean就可以以singleton的方式在多線程中正常工做了。
- 更多詳細參考博客:深刻研究java.lang.ThreadLocal類
5.0.0.6 什麼是線程安全?線程安全有那幾個級別?保障線程安全有哪些手段?ReentrantLock和synchronized的區別?
5.0.0.7 Volatile和Synchronized各自用途是什麼?有哪些不一樣點?Synchronize在編譯時如何實現鎖機制?
5.0.0.8 wait()和sleep()的區別?各自有哪些使用場景?怎麼喚醒一個阻塞的線程?Thread.sleep(0)的做用是啥?
5.0.0.9 同步和非同步、阻塞和非阻塞的概念?分別有哪些使用場景?
-
同步和非同步
- 同步和異步體現的是消息的通知機制:所謂同步,方法A調用方法B後必須等到方法B返回結果才能繼續後面的操做;所謂異步,方法A調用方法B後可以讓方法B在調用結束後經過回調等方式通知方法A
-
阻塞和非阻塞
- 阻塞和非阻塞側重於等待消息時的狀態:所謂阻塞,就是在結果返回以前讓當前線程掛起;所謂非阻塞,就是在等待時可作其餘事情,經過輪詢去詢問是否已返回結果
5.0.1.0 線程的有哪些狀態?請繪製該狀態的流程圖?講一下線程的執行生命週期流程?線程若是出現了運行時異常會怎麼樣?
無限期等待(Waiting):該線程不會被分配CPU執行時間,要等待被其餘線程顯式地喚醒。如下方法會讓線程陷入無限期等待狀態:
```
沒有設置Timeout參數的Object.wait()
沒有設置Timeout參數的Thread.join()
LockSupport.park()
```
- 限期等待(Timed Waiting):該線程不會被分配CPU執行時間,但在必定時間後會被系統自動喚醒。如下方法會讓線程進入限期等待狀態:
```
Thread.sleep()
設置了Timeout參數的Object.wai()
設置了Timeout參數的Thread.join()
LockSupport.parkNanos()
LockSupport.parkUntil()
```
- 阻塞(Blocked):線程被阻塞。和等待狀態不一樣的是,阻塞狀態表示在等待獲取到一個排他鎖,在另一個線程放棄這個鎖的時候發生;而等待狀態表示在等待一段時間或者喚醒動做的發生,在程序等待進入同步區域的時候發生。
- 結束(Terminated):線程已經結束執行
- 繪製該狀態的流程圖
-
線程若是出現了運行時異常會怎麼樣?
- 若是這個異常沒有被捕獲的話,這個線程就中止執行了。另外重要的一點是:若是這個線程持有某個某個對象的監視器,那麼這個對象監視器會被當即釋放
5.0.1.1 synchronized鎖什麼?synchronized同步代碼塊還有同步方法本質上鎖住的是誰?爲何?
-
synchronized鎖什麼
- 對於普通同步方法,鎖是當前實例對象;
- 對於靜態同步方法,鎖是當前類的Class對象;
- 對於同步方法塊,鎖是括號中配置的對象;
- 當一個線程試圖訪問同步代碼塊時,它首先必須獲得鎖,退出或拋出異常時必須釋放鎖。synchronized用的鎖是存在Java對象頭裏的MarkWord,一般是32bit或者64bit,其中最後2bit表示鎖標誌位。
-
本質上鎖住的是對象。
- 在java虛擬機中,每一個對象和類在邏輯上都和一個監視器相關聯,synchronized本質上是對一個對象監視器的獲取。當執行同步代碼塊或同步方法時,執行方法的線程必須先得到該對象的監視器,才能進入同步代碼塊或同步方法;而沒有獲取到的線程將會進入阻塞隊列,直到成功獲取對象監視器的線程執行結束並釋放鎖後,纔會喚醒阻塞隊列的線程,使其從新嘗試對對象監視器的獲取。
5.0.1.2 Volatile實現原理?一個int變量,用volatile修飾,多線程去操做++,線程安全嗎?那如何才能保證i++線程安全?
-
volatile的做用和原理
- Java代碼在編譯後會變成Java字節碼,字節碼被類加載器加載到JVM裏,JVM執行字節碼,最終須要轉化爲彙編指令在CPU上執行。
- volatile是輕量級的synchronized(volatile不會引發線程上下文的切換和調度),它在多處理器開發中保證了共享變量的「可見性」。可見性的意思是當一個線程修改一個共享變量時,另一個線程能讀到這個修改的值。
- 因爲內存訪問速度遠不及CPU處理速度,爲了提升處理速度,處理器不直接和內存進行通訊,而是先將系統內存的數據讀到內部緩存後在進行操做,但操做完不知道什麼時候會寫到內存。普通共享變量被修改以後,何時被寫入主存是不肯定的,當其餘線程去讀取時,此時內存中可能仍是原來的舊值,所以沒法保證可見性。若是對聲明瞭volatile的變量進行寫操做,JVM就會想處理器發送一條Lock前綴的指令,表示將當前處理器緩存行的數據寫回到系統內存。
-
一個int變量,用volatile修飾,多線程去操做++,線程安全嗎
- 技術博客大總結
- 不安全
- 案例代碼,至於打印結果就不展現呢
- volatile只能保證可見性,並不能保證原子性。
-
i++實際上會被分紅多步完成:
- 1)獲取i的值;
- 2)執行i+1;
- 3)將結果賦值給i。
- volatile只能保證這3步不被重排序,多線程狀況下,可能兩個線程同時獲取i,執行i+1,而後都賦值結果2,實際上應該進行兩次+1操做。
private volatile int a = 0;
for (int x=0 ; x<=100 ; x++){
new Thread(new Runnable() {
@Override
public void run() {
a++;
Log.e("小楊逗比Thread-------------",""+a);
}
}).start();
}
-
如何才能保證i++線程安全
- 可使用java.util.concurrent.atomic包下的原子類,如AtomicInteger。其實現原理是採用CAS自旋操做更新值。
for (int x=0 ; x<=100 ; x++){
new Thread(new Runnable() {
@Override
public void run() {
AtomicInteger atomicInteger = new AtomicInteger(a++);
int i = atomicInteger.get();
Log.e("小楊逗比Thread-------------",""+i);
}
}).start();
}
5.0.1.3 CAS原理是什麼?CAS實現原子操做會出現什麼問題?
-
CAS原理是什麼
- CAS即compare and swap的縮寫,中文翻譯成比較並交換。CAS有3個操做數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改成B,不然什麼都不作。自旋就是不斷嘗試CAS操做直到成功爲止。
-
CAS實現原子操做會出現什麼問題
- ABA問題。由於CAS須要在操做之的時候,檢查值有沒有發生變化,若是沒有發生變化則更新,可是若是一個值原來是A,變成,有變成A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,但實際上發生了變化。ABA問題能夠經過添加版本號來解決。Java 1.5開始,JDK的Atomic包裏提供了一個類AtomicStampedReference來解決ABA問題。
- 循環時間長開銷大。pause指令優化。
- 只能保證一個共享變量的原子操做。能夠合併成一個對象進行CAS操做。
5.0.1.4 假若有n個網絡線程,須要當n個網絡線程完成以後,再去作數據處理,你會怎麼解決?
- 多線程同步的問題。這種狀況能夠可使用thread.join();join方法會阻塞直到thread線程終止才返回。更復雜一點的狀況也可使用CountDownLatch,CountDownLatch的構造接收一個int參數做爲計數器,每次調用countDown方法計數器減一。作數據處理的線程調用await方法阻塞直到計數器爲0時。
5.0.1.5 Runnable接口和Callable接口的區別?
-
Runnable接口和Callable接口的區別
- Runnable接口中的run()方法的返回值是void,它作的事情只是純粹地去執行run()方法中的代碼而已;Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合能夠用來獲取異步執行的結果。
- 這實際上是頗有用的一個特性,由於多線程相比單線程更難、更復雜的一個重要緣由就是由於多線程充滿着未知性,某條線程是否執行了?某條線程執行了多久?某條線程執行的時候咱們指望的數據是否已經賦值完畢?沒法得知,咱們能作的只是等待這條多線程的任務執行完畢而已。而Callable+Future/FutureTask卻能夠獲取多線程運行的結果,能夠在等待時間太長沒獲取到須要的數據的狀況下取消該線程的任務,真的是很是有用。
5.0.1.6 若是提交任務時,線程池隊列已滿,這時會發生什麼?線程調度算法是什麼?
-
若是提交任務時,線程池隊列已滿,這時會發生什麼?
- 若是使用的是無界隊列LinkedBlockingQueue,也就是無界隊列的話,不要緊,繼續添加任務到阻塞隊列中等待執行,由於LinkedBlockingQueue能夠近乎認爲是一個無窮大的隊列,能夠無限存聽任務
- 技術博客大總結
- 若是使用的是有界隊列好比ArrayBlockingQueue,任務首先會被添加到ArrayBlockingQueue中,ArrayBlockingQueue滿了,會根據maximumPoolSize的值增長線程數量,若是增長了線程數量仍是處理不過來,ArrayBlockingQueue繼續滿,那麼則會使用拒絕策略RejectedExecutionHandler處理滿了的任務,默認是AbortPolicy
-
線程調度算法是什麼?
- 搶佔式。一個線程用完CPU以後,操做系統會根據線程優先級、線程飢餓狀況等數據算出一個總的優先級並分配下一個時間片給某個線程執行。
5.0.1.7 什麼是樂觀鎖和悲觀鎖?
-
什麼是樂觀鎖和悲觀鎖?
- 樂觀鎖:就像它的名字同樣,對於併發間操做產生的線程安全問題持樂觀狀態,樂觀鎖認爲競爭不老是會發生,所以它不須要持有鎖,將比較-替換這兩個動做做爲一個原子操做嘗試去修改內存中的變量,若是失敗則表示發生衝突,那麼就應該有相應的重試邏輯。
- 悲觀鎖:仍是像它的名字同樣,對於併發間操做產生的線程安全問題持悲觀狀態,悲觀鎖認爲競爭老是會發生,所以每次對某資源進行操做時,都會持有一個獨佔的鎖,就像synchronized,直接上了鎖就操做資源。
5.0.1.8 線程類的構造方法、靜態塊是被哪一個線程調用的?同步方法和同步塊,哪一個是更好的選擇?同步的範圍越少越好嗎?
-
線程類的構造方法、靜態塊是被哪一個線程調用的?
- 線程類的構造方法、靜態塊是被new這個線程類所在的線程所調用的,而run方法裏面的代碼纔是被線程自身所調用的。
-
舉個例子
-
同步方法和同步塊,哪一個是更好的選擇?
- 同步塊,這意味着同步塊以外的代碼是異步執行的,這比同步整個方法更提高代碼的效率。請知道一條原則:同步的範圍越小越好。
- 技術博客大總結
-
同步的範圍越少越好嗎?
- 是的。雖然說同步的範圍越少越好,可是在Java虛擬機中仍是存在着一種叫作鎖粗化的優化方法,這種方法就是把同步範圍變大。這是有用的,比方說StringBuffer,它是一個線程安全的類,天然最經常使用的append()方法是一個同步方法,咱們寫代碼的時候會反覆append字符串,這意味着要進行反覆的加鎖->解鎖,這對性能不利,由於這意味着Java虛擬機在這條線程上要反覆地在內核態和用戶態之間進行切換,所以Java虛擬機會將屢次append方法調用的代碼進行一個鎖粗化的操做,將屢次的append的操做擴展到append方法的頭尾,變成一個大的同步塊,這樣就減小了加鎖-->解鎖的次數,有效地提高了代碼執行的效率。
5.0.1.9 synchonized(this)和synchonized(object)區別?Synchronize做用於方法和靜態方法區別?
-
synchonized(this)和synchonized(object)區別技術博客大總結
- 其實並無很大的區別,synchonized(object)自己就包含synchonized(this)這種狀況,使用的場景都是對一個代碼塊進行加鎖,效率比直接在方法名上加synchonized高一些(下面分析),惟一的區別就是對象的不一樣。
-
對synchronized(this)的一些理解
- 1、當兩個併發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程獲得執行。另外一個線程必須等待當前線程執行完這個代碼塊之後才能執行該代碼塊。
- 2、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另外一個線程仍然能夠訪問該object中的非synchronized(this)同步代碼塊。
- 3、尤爲關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其餘線程對object中全部其它synchronized(this)同步代碼塊的訪問將被阻塞。
- 4、當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就得到了這個object的對象鎖。結果,其它線程對該object對象全部同步代碼部分的訪問都被暫時阻塞。
-
Synchronize做用於方法和靜態方法區別
private void test() {
final TestSynchronized test1 = new TestSynchronized();
final TestSynchronized test2 = new TestSynchronized();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
test1.method01("a");
//test1.method02("a");
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
test2.method01("b");
//test2.method02("a");
}
});
t1.start();
t2.start();
}
private static class TestSynchronized{
private int num1;
public synchronized void method01(String arg) {
try {
if("a".equals(arg)){
num1 = 100;
System.out.println("tag a set number over");
Thread.sleep(1000);
}else{
num1 = 200;
System.out.println("tag b set number over");
}
System.out.println("tag = "+ arg + ";num ="+ num1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static int num2;
public static synchronized void method02(String arg) {
try {
if("a".equals(arg)){
num2 = 100;
System.out.println("tag a set number over");
Thread.sleep(1000);
}else{
num2 = 200;
System.out.println("tag b set number over");
}
System.out.println("tag = "+ arg + ";num ="+ num2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//調用method01方法打印日誌【普通方法】
tag a set number over
tag b set number over
tag = b;num =200
tag = a;num =100
//調用method02方法打印日誌【static靜態方法】
tag a set number over
tag = a;num =100
tag b set number over
tag = b;num =200
- 在static方法前加synchronized:靜態方法屬於類方法,它屬於這個類,獲取到的鎖,是屬於類的鎖。
- 在普通方法前加synchronized:非static方法獲取到的鎖,是屬於當前對象的鎖。 技術博客大總結
- 結論:類鎖和對象鎖不一樣,synchronized修飾不加static的方法,鎖是加在單個對象上,不一樣的對象沒有競爭關係;修飾加了static的方法,鎖是加載類上,這個類全部的對象競爭一把鎖。
其餘介紹
01.關於博客彙總連接
02.關於個人博客