學習筆記,自用,整理中
摘要:面
sychronied修飾普通方法和靜態方法的區別?什么是可見性?
對象鎖是用于對象實例方法,或者一個對象實例上的,類鎖是用于類的靜態方法或者一個類的class對象上的。我們知道,類的對象實例可以有很多個,但是每個類只有一個class對象,所以不同對象實例的對象鎖是互不干擾的,但是每個類只有一個類鎖。
但是有一點必須注意的是,其實類鎖只是一個概念上的東西,并不是真實存在的,類鎖其實鎖的是每個類的對應的class對象。類鎖和對象鎖之間也是互不干擾的。
可見性是指當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值。
由于線程對變量的所有操作都必須在工作內存中進行,而不能直接讀寫主內存中的變量,那么對于共享變量V,它們首先是在自己的工作內存,之后再同步到主內存。可是并不會及時的刷到主存中,而是會有一定時間差。很明顯,這個時候線程 A 對變量 V 的操作對于線程 B 而言就不具備可見性了 。
要解決共享對象可見性這個問題,我們可以使用volatile關鍵字或者是加鎖。
鎖分哪幾類?
CAS無鎖編程的原理。
使用當前的處理器基本都支持CAS()的指令,只不過每個廠家所實現的算法并不一樣,每一個CAS操作過程都包含三個運算符:一個內存地址V,一個期望的值A和一個新值B,操作的時候如果這個地址上存放的值等于這個期望的值A,則將地址上的值賦為新值B,否則不做任何操作。
CAS的基本思路就是,如果這個地址上的值和期望的值相等,則給其賦予新值,否則不做任何事兒,但是要返回原值是多少。循環CAS就是在一個循環里不斷的做cas操作,直到成功為止。
還可以說說CAS的三大問題。
ReentrantLock的實現原理。
線程可以重復進入任何一個它已經擁有的鎖所同步著的代碼塊,synchronized、ReentrantLock都是可重入的鎖。在實現上,就是線程每次獲取鎖時判定如果獲得鎖的線程是它自己時,簡單將計數器累積即可,每 釋放一次鎖,進行計數器累減,直到計算器歸零,表示線程已經徹底釋放鎖。
底層則是利用了JUC中的AQS來實現的。
AQS原理?(小米?京東)
是用來構建鎖或者其他同步組件的基礎框架,比如ReentrantLock、ReentrantReadWriteLock和CountDownLatch就是基于AQS實現的。它使用了一個int成員變量表示同步狀態,通過內置的FIFO隊列來完成資源獲取線程的排隊工作。它是CLH隊列鎖的一種變體實現。它可以實現2種同步方式:獨占式,共享式。
AQS的主要使用方式是繼承,子類通過繼承AQS并實現它的抽象方法來管理同步狀態,同步器的設計基于模板方法模式,所以如果要實現我們自己的同步工具類就需要覆蓋其中幾個可重寫的方法,如tryAcquire、tryReleaseShared等等。
這樣設計的目的是同步組件(比如鎖)是面向使用者的,它定義了使用者與同步組件交互的接口(比如可以允許兩個線程并行訪問),隱藏了實現細節;同步器面向的是鎖的實現者,它簡化了鎖的實現方式,屏蔽了同步狀態管理、線程的排隊、等待與喚醒等底層操作。這樣就很好地隔離了使用者和實現者所需關注的領域。
在內部,AQS維護一個共享資源state,通過內置的FIFO來完成獲取資源線程的排隊工作。該隊列由一個一個的Node結點組成,每個Node結點維護一個prev引用和next引用,分別指向自己的前驅和后繼結點,構成一個雙端雙向鏈表。
Synchronized的原理以及與ReentrantLock的區別。(360)
synchronized (this)原理:涉及兩條指令:monitorenter,monitorexit;再說同步方法,從同步方法反編譯的結果來看,方法的同步并沒有通過指令monitorenter和monitorexit來實現,相對于普通方法,其常量池中多了ACC_SYNCHRONIZED標示符。
JVM就是根據該標示符來實現方法的同步的:當方法被調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標志是否被設置,如果設置了,執行線程將先獲取monitor,獲取成功之后才能執行方法體,方法執行完后再釋放monitor。在方法執行期間,其他任何線程都無法再獲得同一個monitor對象。
Synchronized做了哪些優化?(京東)
引入如自旋鎖、適應性自旋鎖、鎖消除、鎖粗化、偏向鎖、輕量級鎖、逃逸分析
等技術來減少鎖操作的開銷。
逃逸分析
如果證明一個對象不會逃逸方法外或者線程外,則可針對此變量進行優化:
同步消除synchronization Elimination,如果一個對象不會逃逸出線程,則對此變量的同步措施可消除。
鎖消除和粗化
鎖消除:虛擬機的運行時編譯器在運行時如果檢測到一些要求同步的代碼上不可能發生共享數據競爭,則會去掉這些鎖。
鎖粗化:將臨近的代碼塊用同一個鎖合并起來。
消除無意義的鎖獲取和釋放,可以提高程序運行性能。
Synchronized?static與非static鎖的區別和范圍(小米)
對象鎖是用于對象實例方法,或者一個對象實例上的,類鎖是用于類的靜態方法或者一個類的class對象上的。我們知道,類的對象實例可以有很多個,但是每個類只有一個class對象,所以不同對象實例的對象鎖是互不干擾的,但是每個類只有一個類鎖。
但是有一點必須注意的是,其實類鎖只是一個概念上的東西,并不是真實存在的,類鎖其實鎖的是每個類的對應的class對象。類鎖和對象鎖之間也是互不干擾的。
volatile?能否保證線程安全?在DCL上的作用是什么?
不能保證,在DCL的作用是:volatile是會保證被修飾的變量的可見性和 有序性,保證了單例模式下,保證在創建對象的時候的執行順序一定是
1.分配內存空間
2.實例化對象instance
3.把instance引用指向已分配的內存空間,此時instance有了內存地址,不再為null了
的步驟, 從而保證了instance要么為null 要么是已經完全初始化好的對象。
volatile和synchronize有什么區別?(B站?小米?京東)
volatile是最輕量的同步機制。
volatile保證了不同線程對這個變量進行操作時的可見性,即一個線程修改了某個變量的值,這新值對其他線程來說是立即可見的。但是volatile不能保證操作的原子性,因此多線程下的寫復合操作會導致線程安全問題。
關鍵字synchronized可以修飾方法或者以同步塊的形式來進行使用,它主要確保多個線程在同一個時刻,只能有一個線程處于方法或者同步塊中,它保證了線程對變量訪問的可見性和排他性,又稱為內置鎖機制。
什么是守護線程?你是如何退出一個線程的?
Daemon(守護)線程是一種支持型線程,因為它主要被用作程序中后臺調度以及支持性工作。這意味著,當一個Java虛擬機中不存在非Daemon線程的時候,Java虛擬機將會退出。可以通過調用Thread.setDaemon(true)將線程設置為Daemon線程。我們一般用不上,比如垃圾回收線程就是Daemon線程。
線程的中止:
要么是run執行完成了,要么是拋出了一個未處理的異常導致線程提前結束。
暫停、恢復和停止操作對應在線程Thread的API就是suspend()、resume()和stop()。但是這些API是過期的,也就是不建議使用的。因為會導致程序可能工作在不確定狀態下。
安全的中止則是其他線程通過調用某個線程A的interrupt()方法對其進行中斷操作,被中斷的線程則是通過線程通過方法isInterrupted()來進行判斷是否被中斷,也可以調用靜態方法Thread.interrupted()來進行判斷當前線程是否被中斷,不過Thread.interrupted()會同時將中斷標識位改寫為false。
sleep?、wait、yield?的區別,wait?的線程如何喚醒它?(東方頭條)
yield()方法:使當前線程讓出CPU占有權,但讓出的時間是不可設定的。也不會釋放鎖資源。所有執行yield()的線程有可能在進入到就緒狀態后會被操作系統再次選中馬上又被執行。
yield() 、sleep()被調用后,都不會釋放當前線程所持有的鎖。
調用wait()方法后,會釋放當前線程持有的鎖,而且當前被喚醒后,會重新去競爭鎖,鎖競爭到后才會執行wait方法后面的代碼。
Wait通常被用于線程間交互,sleep通常被用于暫停執行,yield()方法使當前線程讓出CPU占有權。
wait 的線程使用notify/notifyAll()進行喚醒。
sleep是可中斷的么?(小米)
sleep本身就支持中斷,如果線程在sleep期間被中斷,則會拋出一個中斷異常。
線程生命周期。
Java中線程的狀態分為6種:
1. 初始(NEW):新創建了一個線程對象,但還沒有調用start()方法。
2. 運行(RUNNABLE):Java線程中將就緒(ready)和運行中(running)兩種狀態籠統的稱為“運行”。
線程對象創建后,其他線程(比如main線程)調用了該對象的start()方法。該狀態的線程位于可運行線程池中,等待被線程調度選中,獲取CPU的使用權,此時處于就緒狀態(ready)。就緒狀態的線程在獲得CPU時間片后變為運行中狀態(running)。
3.?阻塞(BLOCKED):表示線程阻塞于鎖。
4.?等待(WAITING):進入該狀態的線程需要等待其他線程做出一些特定動作(通知或中斷)。
5.?超時等待(TIMED_WAITING):該狀態不同于WAITING,它可以在指定的時間后自行返回。
6. 終止(TERMINATED):表示該線程已經執行完畢。
ThreadLocal是什么?
ThreadLocal是Java里一種特殊的變量。ThreadLocal為每個線程都提供了變量的副本,使得每個線程在某一時間訪問到的并非同一個對象,這樣就隔離了多個線程對數據的數據共享。
在內部實現上,每個線程內部都有一個ThreadLocalMap,用來保存每個線程所擁有的變量副本。
線程池基本原理。
在開發過程中,合理地使用線程池能夠帶來3個好處。
第一:降低資源消耗。第二:提高響應速度。第三:提高線程的可管理性。
1)如果當前運行的線程少于corePoolSize,則創建新線程來執行任務(注意,執行這一步驟需要獲取全局鎖)。
2)如果運行的線程等于或多于corePoolSize,則將任務加入BlockingQueue。
3)如果無法將任務加入BlockingQueue(隊列已滿),則創建新的線程來處理任務。
4)如果創建新線程將使當前運行的線程超出maximumPoolSize,任務將被拒絕,并調用RejectedExecutionHandler.rejectedExecution()方法。
有三個線程T1,T2,T3,怎么確保它們按順序執行?
可以用join方法實現。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
具體:
家里電腦-F:\學習\【01】java筑基\(09)2020.5.19 深入理解并發編程和歸納總結 02---mark\20.5.19 筆記&代碼\并發編程筆記里面有詳細說明