1、Synchronized關鍵字
1、方法中的變量不存在非線程安全問題,都是線程安全的。
2、兩個線程訪問同一個對象中的同步方法時,一定是線程安全的。
3、關鍵字synchronized取得的鎖都是對象鎖,而不是把一段代碼或方法(函數)當作鎖,哪個線程先執(zhí)行synchronized關鍵字的方法,哪個線程就持有該方法所屬對象的鎖Lock,那么其他線程只能呈等待狀態(tài),前提是多個線程訪問的是同一個對象。但如果多個線程訪問多個對象,則JVM會創(chuàng)建多個鎖。
4、調用關鍵字synchronized聲明的方法一定是排隊運行的。另外需要牢記“共享”兩個字,只有共享資源的讀寫訪問才需要同步化,如果不是共享資源,那么根本沒同步的必要。
5、兩個線程A和B,雖然線程A先持有了Object對象的鎖,但線程B完全可以異步調用非synchronized類型的方法。
6、A線程先持有Object對象的Lock鎖,B線程可以以異步的方式調用object對象中的非synchronized類型的方法。
7、A線程先持有object對象的Lock鎖,B線程如果在這時調用Object對象中的synchronized類型的方法則需等待,也就是同步。
8、多個線程調用同一個方法時,為了避免數據出現交叉的情況,使用synchronized關鍵字進行同步。
9、臟讀:在讀取實例變量時,此值已經被其他線程更改過了。
10、當A線程調用anyObject對象加入synchronized關鍵字的X方法時,A線程就獲得了X方法鎖,更準確的講,是獲得了對象的鎖,所以其他線程必須等A線程執(zhí)行完畢才可以調用X方法,但B線程可以隨意調用其他的非synchronized同步方法。
11、當A線程調用anyObject對象加入synchronized關鍵字的X方法是,A線程就獲得了X方法所在的對象鎖,所以其他線程必須等A線程執(zhí)行完畢才可以調用X方法,而B線程如果調用聲明了synchronized關鍵字的非X方法時,必須等A線程將X方法執(zhí)行完,也就是釋放對象鎖后調用。
12、臟讀一定會出現在操作實例變量的情況下,這就是不同線程爭搶實例變量的結果。
13、synchronized鎖重入:關鍵字synchronized擁有鎖重入的功能,也就是在使用synchronized時,當一個線程得到一個對象鎖后,再次請求此對象鎖時是可以再次得到該對象的鎖的。這也證明在一個synchronized方法/塊內部調用本類其他synchronized方法/塊時,是永遠可以得到鎖的。
14、當存在父子關系時,子類是完全可以通過“可重入鎖”調用父類的同步方法的。
15、出現異常,鎖自動釋放。
16、同步不具有繼承性。
17、當兩個并發(fā)線程訪問同一個對象object的synchronized(this)同步代碼塊時,一段時間只能有一個線程被執(zhí)行,另一個線程必須等待當前線程執(zhí)行完這個代碼以后才能執(zhí)行改代碼塊。
18、當一個線程訪問object的synchronized同步代碼塊時,另一個線程仍然可以訪問object中的非synchronized(this)同步代碼塊。
19、當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對同一個object中所有其他synchronized(this)同步代碼塊的訪問將被阻塞。這說明synchronized使用的“對象監(jiān)視器”是一個。
20、和synchronized方法一樣,synchronized(this)代碼塊也是鎖當前對象的。
21、使用synchronized(非this對象X)格式同步代碼時,對象監(jiān)視器必須是同一個對象。否則,結果就是異步調用,會交叉運行。
22、synchronized加到static靜態(tài)方法上,是給Class上鎖,加到非static靜態(tài)方法上,是給對象上鎖。
23、同步synchronized(class)的作用實際上和synchronized static的作用類似。
24、JVM中具有Spring常量池緩存的功能

25、在將任何數據類型作為同步鎖時,需要注意的是,是否有多個線程同時持有鎖對象,如果同時持有相同的鎖對象,則這些線程之間就是同步的。如果分別獲得所對象,則這些線程之間就是異步的。
26、只有鎖對象不變,即使對象的屬性變化,那么線程之前還是同步的。
在此我向大家推薦一個架構學習交流群。交流學習群號:575745314 里面會分享一些資深架構師錄制的視頻錄像:有Spring,MyBatis,Netty源碼分析,高并發(fā)、高性能、分布式、微服務架構的原理,JVM性能優(yōu)化、分布式架構等這些成為架構師必備的知識體系。還能領取免費的學習資源,目前受益良多
2、volatile關鍵字
1、volatile可以使變量在多個線程間可見。
2、強制從公共堆棧中取得變量的值,而不是從線程私有數據棧中獲取數據。
3、增加了實例變量在多個線程之間的可見性,但是不支持原子性。
4、使用場合是在多個線程中可以感知實例變量被更改了,并且可以獲取最新的值使用,也就是用多線程獲取共享變量時,可以獲得最新值使用。
3、面試題
1、什么是線程,什么是進程,它們有什么區(qū)別和聯系,一個進程里面是否必須有個線程
進程本質是一個正在執(zhí)行的程序,一個進程可以有多個線程.線程是進程的最小執(zhí)行單位,一個進程至少有一個線程
區(qū)別:1:多進程程序不受java控制,而多線程則受java控制,
2:多線程比多進程需要更少的管理成本
2、實現一個線程有哪幾種方式,各有什么優(yōu)缺點,比較常用的是那種,為什么
實現一個線程有三種方式:
1) ? ? ?繼承Thread類,需要重寫run方法,無返回值
a) ? ? ? ?優(yōu)點:可以直接調用start方法啟動線程
b) ? ? ? ?缺點:java只能單繼承,如果已經有了父類,不能用這種方法
2) ? ? ?實現Runnable接口,需要重寫run()方法
a) ? ? ? ?優(yōu)點:即使自己定義的線程類有了父類也可以實現接口,而且接口是多實現
b) ? ? ? ?缺點:需通過構造一個Thread把自己傳進去,才能實現Thread的方法,代碼復雜
3) ? ? ?實現Callable接口,需要重寫call()方法
a) ? ? ? ?優(yōu)點:可以拋出異常,有返回值
b) ? ? ? ?缺點:只有jdk1.5以后才支持,結合FuntureTask和Thread類一起使用,最后調用start啟動線程
一般用第二種,實現Runnable接口,比較方便,擴展性高
3、一般情況下我們實現自己線程時候要重寫什么方法
使用Thread類,要重寫run()方法
實現Runnable接口時,要實現run()方法
使用Callable接口時,要重寫call()方法
4、start方法和run方法有什么區(qū)別,我們一般調用的那個方法,系統調用的是那個方法
Start用來啟動線程
當調用start后,線程并不會馬上運行,而是處于就緒狀態(tài),是否要運行取決于cpu
Run用來子類重寫來實現線程的功能
我們一般調用的是start方法,系統調用的是run方法
5、sleep方法有什么作用,一般用來做什么
Sleep是一個Thread類的靜態(tài)方法
作用:讓調用它的線程休眠指定的時間,用于暫停線程,但不會把線程鎖讓給其它線程,休眠時間結束,線程進入就緒狀態(tài),等待cpu分配的執(zhí)行機會
6、講下join,yield方法的作用,以及什么場合用它們
Join()有嚴格的先后順序,調用它的線程需要執(zhí)行完,其它線程才會執(zhí)行
Yield()是暫停當前正在執(zhí)行的線程對象,把時間讓給其它線程
7、線程中斷是否能直接調用stop,為什么?
線程終端不能直接調用stop()方法
Stop()方法是從外部強行終止一個線程,會導致不可預知的錯誤,比如線程鎖沒有歸還,io流不能關流
線程只能調用interrupt()方法中斷,而且不是立即中斷,(
只是發(fā)出了一個類似于信號量的東西,通過修改了被調用線程的中斷狀態(tài)來告知那個線程,說它被中斷了,至于什么時候中斷,這個系統會判斷)系統會在一個合適的時候進行中斷處理
8、列舉出一般情況下線程中斷的幾種方式,并說明他們之間的優(yōu)缺點,并且說明那種中斷方式最好
線程中斷有4種方式
由interrupt發(fā)出中斷信號,用戶接收中斷信號,通過isInterrupted判斷線程是否中斷
由interrupt發(fā)出中斷信號,系統接收中斷信號,通過sleep拋出中斷異常,并把中斷信號清除,只能拋出一次
用戶自定義中斷,將中斷信號發(fā)出,自己接收該中斷信號
調用interrupted(),會把中斷信號清除,并中斷線程
9、線程有幾種狀態(tài),他們是怎么轉化的
線程一般分為:新生,就緒,運行,阻塞,死亡五中狀態(tài)
當創(chuàng)建一個線程后,并沒有運行,處于新生狀態(tài),需要通過調用start方法,讓線程處于就緒狀態(tài),但是否運行取決于cpu分配的執(zhí)行機會,當得到cpu的執(zhí)行機會后馬上運行,一個正在執(zhí)行的線程可以通過很多方式進入阻塞狀態(tài),當執(zhí)行完所有操作后就進入死亡狀態(tài)
10、在實現Runnable的接口中怎么樣訪問當前線程對象,比如拿到當前線程的名字
通過Thread.currentThread().getName()可獲得當前線程名字
11、講下什么是守護線程,以及在什么場合來使用它
守護線程是用來監(jiān)聽其它線程是否掛掉,這個線程具有最低優(yōu)先級
用于為系統中的其它線程提供服務
守護線程就像象棋里的車馬相仕,非守護線程就是老帥,老帥掛掉,守護線程也就掛掉了
當主線程和主線程創(chuàng)建的子線程全部退出,守護線程一定會跟著退出
(比如QQ主程序和QQ聊天窗口,主程序退出,QQ聊天窗口也隨之關掉)
12、一般的線程優(yōu)先級是什么回事,線程優(yōu)先級高的線程一定會先執(zhí)行嗎?如果不設置優(yōu)先級的話,那么線程優(yōu)先級是多少,設置線程優(yōu)先級用那個函數
線程的優(yōu)先級就是設置哪個線程先執(zhí)行,但不是絕對,只是讓優(yōu)先級高的線程優(yōu)先運行的概率高一些,
線程默認優(yōu)先級是NORM_PRIORITY = 5;
最小是 MIN_PRIORITY = 1;
最大是 MAX_PRIORITY = 10;
設置線程優(yōu)先級有setPriority()方法
13、為什么Thread里面的大部分方法都是final的
線程很多方法都是由系統調用的,不能通過子類重寫去改變他們的行為
14、什么是線程同步,什么是線程安全
同步:當兩個或兩個以上的線程需要共享資源,通過同步方法限制資源在一次僅被一個線程占用
線程安全:線程安全就是多線程操作同一個對象不會產生數據污染,
線程同步一般來保護線程安全,final修飾的也是線程安全
15、講下同步方法和同步塊的區(qū)別,以及什么時候用它們
同步方法:被synchronized修飾的方法,同步整個方法,且整個方法都會被鎖住,同一時間只有一個線程可以訪問該方法,
同步塊: 被synchronized修飾的代碼塊,可以同步一小部分代碼
什么時候用?
同步塊越小性能越好,當性能要求比較高,用同步代碼塊
16、簡單說下Lock對象的實現類的鎖機制和同步方法或同步塊有什么區(qū)別
是JDK1.5才出現的,Lock對象比synchronized更加靈活,可以控制什么時候上鎖,什么時候解鎖,而使用synchronized必須等代碼執(zhí)行完才會解鎖.
Synchronized在鎖定時如果方法塊拋出異常,JVM會自動釋放鎖,而Lock出現異常必須在finally將鎖釋放,否則將引起死鎖
17、同步塊里面的同步監(jiān)視器是怎么寫的,默認的同步方法里面的同步監(jiān)視器是那個
Synchronized(對象){
//代碼塊
}
默認的同步監(jiān)視器是this
18、講下什么是死鎖,死鎖發(fā)生的幾個條件是什么
死鎖就是當有兩個或兩個以上的線程都獲得對方的資源,但彼此都不肯放開,處于僵持階段,此時就造成了死鎖
條件:兩個或兩個以上的線程,同時想要獲得對方的資源,彼此又不肯放開
19、線程間是怎么通信的,通過調用幾個方法來交互的
線程是通過wait , notify等方法相互作用進行協作通信;
wait()方法使得當前線程必須要等待,直到到另外一個線程調用notify()或者notifyAll()方法喚醒
-- wait()和notify()方法要求在調用時線程已經獲得了對象鎖,因此對這兩個方法的調用需要放在synchronized修飾的方法或代碼塊中。
20、wait,notify,notifyAll在什么地方使用才有效,他們是那個類的方法
Wait,notify,notifyAll都必須在synchronized修飾的方法或代碼塊中使用,都屬于Object的方法,可以被所有類繼承,都是final修飾的方法,不能通過子類重寫去改變他們的行為
21、wait和sleep有什么區(qū)別和聯系,他們執(zhí)行的時候是否都會釋放鎖
Wait和sleep都可以使線程暫停,但wait必須在synchronized修飾的方法或代碼塊中使用,
Sleep()方法是線程類的靜態(tài)方法,調用此方法會讓當前線程暫停執(zhí)行指定的時間,將執(zhí)行機會(CPU)讓給其它線程,但是對象的鎖依然保持,因此休眠時間結束后會自動恢復(就緒狀態(tài)),
wait()是Object類的方法,調用對象的wait()方法會讓當前線程放棄對象的鎖(線程暫停執(zhí)行),進入對象的等待池(wait pool),只有調用對象的Notify()或notifyAll()方法才能喚醒等待池中的線程進入等鎖池(lock pool),如果線程重新獲得對象的鎖就可以進入就緒狀態(tài)
22、yield,sleep方法有什么區(qū)別和聯系
Yield和sleep都可以讓線程暫停,
1) ? ? ?Sleep()方法給其它線程運行機會時不考慮線程的優(yōu)先級,因此會給低優(yōu)先級的線程以運行機會…yield()方法只會給相同優(yōu)先級或更高優(yōu)先級的線程以運行機會
2) ? ? ?線程執(zhí)行sleep()方法后轉入阻塞狀態(tài),而執(zhí)行yield()方法后轉入就緒狀態(tài)
3) ? ? ?Sleep()方法聲明拋出InterruptedException,而yield()方法沒有聲明任何異常
4) ? ? ?Sleep()方法比yield()方法具有更好的可移植性
23、線程的啟動是哪個方法,調用的是哪個方法?
Start用于啟動線程,當調用start后,線程并不會馬上運行,而是處于就緒狀態(tài),是否運行取決于cpu給的時間片
Run()方法用于子類重寫來實現線程的功能
我們一般調用的是start方法,系統調用的是run方法
24、線程安全與線程不安全的區(qū)別
線程安全就是多線程訪問時,采用了加鎖機制,
當一個線程訪問類中某個數據,進行保護,其它線程不能進行訪問,直到該線程執(zhí)行完,其它線程才可使用---不會導致數據污染
線程不安全就是不提供數據訪問保護,有可能出現多個線程先后更改數據造成所得到的數據是臟數據
25、線程的實現方式,線程的生命周期等
實現方式
1) ?通過繼承Thread類
2) ?實現Runnable接口
3) ?實現Callable接口
生命周期
新建
就緒
運行
堵塞
死亡
26、如何處理線程不安全問題(有2種解決方法)
第一,是采用原子變量,線程安全問題最根本上是由于全局變量和靜態(tài)變量引起的,定義變量用sig_atomic_t和volatile。
第二,就是實現線程間同步,讓線程有序訪問變量
27、線程中常用方法的區(qū)別
線程中用到最多的是start方法,它的作用是啟動一個線程
Sleep()讓線程休眠一會,但不會釋放鎖
Join()強制執(zhí)行一個線程
Wait()是Object類中的方法,會讓當前線程放棄對象鎖,進入對象等待池
Yield()禮讓線程,讓另外一個線程執(zhí)行一會,自己再執(zhí)行,只能讓同優(yōu)先級的線程有執(zhí)行的機會
currentThread()用于獲得當前線程對象,可以調用getName()方法獲得當前線程名字
28、多線程和單線程有什么區(qū)別?
干一件事情一個人干和兩個人同時干的區(qū)別
29、線程的5種狀態(tài),__、_、_、___。啟動調用_方法,啟動后會調用__方法。
新生
就緒
運行
阻塞
死亡
啟動調用start()
30、用同步塊與同步方法的區(qū)別?
同步方法是被synchronized修飾的方法,同步整個方法, ,同一時間只有一個線程可以訪問該方法
同步塊是被synchronized修飾的代碼塊,可以同步一小部分代碼
同步代碼越少性能越好,當性能要求比較高時,用同步塊
31、寫二個線程,對一個int類型一個i++,一個i--
32、說說stop為什么不建議使用。
Stop()方法是從外部強行終止一個線程,會導致不可預知的錯誤,比如線程鎖沒有歸還,io流不能關流
線程只能調用interrupt()方法中斷,而且不是立即中斷,(
只是發(fā)出了一個類似于信號量的東西,通過修改了被調用線程的中斷狀態(tài)來告知那個線程,說它被中斷了,至于什么時候中斷,這個系統會判斷)系統會在一個合適的時候進行中斷處理
33、Runable接口的方法是什么?
Run
34、什么是同步和異步,分別用例子說明,同步有幾種方式?
同步是排隊去做事情,異步就是各做各的
35、什么是對象鎖?
1. ? java中的每個對象都有一個鎖,當訪問某個對象的synchronized方法時,表示將該對象上鎖,此時其他任何線程都無法在去訪問該syncronized 方法了,直到之前的那個線程執(zhí)行方法完畢后,其他線程才有可能去訪問該synchronized方法。
2.如果一個對象有多個synchronized方法,某一時刻某個線程已經進入到某個synchronzed方法,那么在該方法沒有執(zhí)行完畢前,其他線程無法訪問該對象的任何synchronzied 方法的,但可以訪問非synchronzied方法。
3.如果synchronized方法是static的,那么當線程訪問該方法時,它鎖的并不是synchronized方法所在的對象,而是synchuronized方法所在對象的對應的Class對象,
[類鎖(synchronized修飾的靜態(tài)方法)]
36、什么是死鎖?
死鎖就是當有兩個或兩個以上的線程都獲得對方的資源,但彼此有不肯放開,處于僵持狀態(tài),此時便造成了死鎖。