經驗整理-1-多線程高併發-100-@

參考路人甲java系列:https://www.cnblogs.com/itsoku123/p/11424473.html

 

12.4.3 狀態轉換、線程狀態的流轉

 

線程之間怎麼通訊的?

1、volatile修飾符:全局變量方式,最簡單的一種方法是使用全局變量
2、使用Object類的wait() 和 notify() 方法:Object類提供了線程間通信的方法:wait()、notify()、notifyaAl(),它們是多線程通信的基礎,而這種實現方式的思想自然是線程間通信。
3、JUC工具類 CountDownLatch:信號量等通知
4、使用 ReentrantLock 結合 Condition:
5、消息隊列
6 redis

 

Volatile非原子性的原因?


----------高併發多線程----------
?多線程用到的場景?

1、前期的補償通知機制是用線程池newScheduledThreadPool 開啓的多線和去通知的。
2、上層來調網關的一些業務邏輯處理慢的接口,先快速返回,開啓線程異步處理業務邏輯,處理完了之後再通知上層業務方

3、mysql主從備份原理也是線程異步去實現的

-------------------ThreadLocal-------------
?ThreadLocal工作原理或實現原理?

https://www.cnblogs.com/kancy/p/10702310.html
底層也是封裝了ThreadLocalMap集合類來綁定當前線程和變量副本的關係,以threadlocal實例爲key,其他信息爲value,保證各個線程獨立並且訪問安全!
ThreadLocalMap
?ThreadLocal的作用或優點或應用場景?
Mybatis高併發數據庫連接操作,都會使用ThreadLocal類來保證Java多線程程序訪問和數據庫數據的一致性問題.
?我搭建過,如何搭建或如何使用?


-------------------Volatile-------------
?Volatile工作原理或實現原理?

 

?Volatile的作用或優點或應用場景?
作用是變量在多個線程之間可見,就可以通訊了


-------------------鎖-------------

1)wait方法----當interrupt方法遇到---嚴格不算是鎖

Object o= new Object();
try { o.wait();} catch (InterruptedException e) {}//thread裏執行了wait,釋放資源wait進入等待隊列
wait(long)//過這個時間則自動喚醒。
thread.interrupt();//線程打斷interrupt喚醒wait
o.notify();//對象喚醒,調用notify()方法一次只隨機通知一個線程進行喚醒。
o.notifyAll();//對象喚醒,所有線程進行喚醒。

(sleep不需要喚醒,佔着資源呢)
 

2)synchronized 是一種悲觀鎖,阻塞的可重入鎖
synchronized (lock) {}

3)lock 是CAS(AQS)樂觀鎖,用tryLock方式是非阻塞的可重入鎖
private Lock lock = new ReentrantLock();


方式一: lock.lock();
方式二 : if (lock.tryLock()) { lock.lock(); try { } catch (Exception e) { } finally { lock.unlock(); }


 

?synchronized是線程安全的嗎?

是。線程安全性包括兩個方面,①可見性。②原子性。有的鎖可以用lock代替。

 

--------------------------------------------------------------------線程池--------------------------------------------------------------------

 

什麼是線程池

線程池是一個線程集合,線程池是運用場景最多的併發框架需要異步或併發執行任務的程序都可以使用線程池。在開發過程中,合理地使用程池能夠帶3個好
第一:降低源消耗。通重複利用已創建的線程降低線程創建和銷燬造成的消耗。
第二:提高響速度。當任到達,任可以不需要等到線程創建就能立即執行
第三:提高程的可管理性程是稀缺源,如果無限制地創建,不僅會消耗系統資源,
還會降低系統的穩定性,使用線程池可以進行統一分配、調優和監控
。但是,要做到合理利用
程池,必須對實現原理了如指掌。

線程池作用

減少了創建和銷燬線程所需的時間,從而提高效率

?巧記四種線程池?

1)公共創建頭:ExecutorService newExecutorService = Executors.new*Thread*--------EE
2)四種特殊線程池:------即ssfc(姍姍FC)
Single--------newSingleThreadExecutor總結:用唯一的工作線程來執行任務,結果依次輸出,相當於順序執行各個任務
S-------newScheduledThreadPool總結:定長線程池,支持定時及週期性任務執行
F---------newFixedThreadPool 總結:
創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待
C-------------newCachedThreadPool 總結:線程池爲無限大,當執行第二個任務時第一個任務已經完成,會複用執行第一個任務的線程,而不用每次新建線程。

 

線程原理剖析

提交一個任務到線程池中,線程池的處理流程如下:

1、判斷線程池裏的核心線程是否都在執行任務,如果不是(核心線程空閒或者還有核心線程沒有被創建)則創建一個新的工作線程來執行任務。如果核心線程都在執行任務,則進入下個流程。

2、線程池判斷工作隊列是否已滿,如果工作隊列沒有滿,則將新提交的任務存儲在這個工作隊列裏。如果工作隊列滿了,則進入下個流程。

3、判斷線程池裏的線程是否都處於工作狀態,如果沒有,則創建一個新的工作線程來執行任務。如果已經滿了,則交給飽和策略來處理這個任務。

合理配置線程池

要想合理的配置線程池,就必須首先分析任務特性,可以從以下幾個角度來進行分析:

任務的性質:CPU密集型任務,IO密集型任務和混合型任務。

任務的優先級:高,中和低。

任務的執行時間:長,中和短。

任務的依賴性:是否依賴其他系統資源,如數據庫連接。

任務性質不同的任務可以用不同規模的線程池分開處理。CPU密集型任務配置儘可能少的線程數量,如配置Ncpu+1個線程的線程池。IO密集型任務則由於需要等待IO操作,線程並不是一直在執行任務,則配置儘可能多的線程,如2*Ncpu。混合型的任務,如果可以拆分,則將其拆分成一個CPU密集型任務和一個IO密集型任務,只要這兩個任務執行的時間相差不是太大,那麼分解後執行的吞吐率要高於串行執行的吞吐率,如果這兩個任務執行時間相差太大,則沒必要進行分解。我們可以通過Runtime.getRuntime().availableProcessors()方法獲得當前設備的CPU個數。

優先級不同的任務可以使用優先級隊列PriorityBlockingQueue來處理。它可以讓優先級高的任務先得到執行,需要注意的是如果一直有優先級高的任務提交到隊列裏,那麼優先級低的任務可能永遠不能執行。

執行時間不同的任務可以交給不同規模的線程池來處理,或者也可以使用優先級隊列,讓執行時間短的任務先執行。

依賴數據庫連接池的任務,因爲線程提交SQL後需要等待數據庫返回結果,如果等待的時間越長CPU空閒時間就越長,那麼線程數應該設置越大,這樣才能更好的利用CPU。

一般總結哦,有其他更好的方式,希望各位留言,謝謝。

 

CPU密集型時,任務可以少配置線程數,大概和機器的cpu核數相當,這樣可以使得每個線程都在執行任務

IO密集型時,大部分線程都阻塞,故需要多配置線程數,2*cpu核數

操作系統之名稱解釋:

某些進程花費了絕大多數時間在計算上,而其他則在等待I/O上花費了大多是時間,

前者稱爲計算密集型(CPU密集型)computer-bound,後者稱爲I/O密集型,I/O-bound。

 

淺談Java中的深克隆和淺克隆

淺克隆:對於非基本類型字段屬性,只拷貝對象內存地址,修改該字段會影響原字段對象--類實現Cloneable接口
深克隆:對於非基本類型字段屬性,會重新創建一個對象,使用新的內存地址--實現clone()方法後,對非基本類型的對象類屬性字段,創建新對象,重新賦值

 

----------------併發包相關--------------------------------------

自旋鎖和互斥鎖的區別,java中lock Syncronized區別

1、自旋鎖---busy-waiting,自旋鎖是未獲得鎖的線程(對外被阻塞-但自旋鎖是一種非阻塞鎖,因爲它沒睡眠),但會死循環檢測,一直消耗cpu,直到獲得別人釋放的鎖(copy_to_user之類的接口可能造成死鎖)------無限檢測循環中,執行一個 CAS 操作,直到成功
2、互斥鎖---sleep-waiting,未獲得鎖的線程會sleep(被阻塞),放入等待隊列裏,直到獲得別人釋放的鎖--synchronized悲觀鎖

3、可重入鎖就是遞歸鎖,ReentrantLock和synchronized都是可重入鎖
4、公平鎖--公平鎖就是保障了多線程下各線程獲取鎖的順序,先到的線程優先獲取鎖,而非公平鎖則無法提供這個保障
5、可中斷鎖:顧名思義,就是可以相應中斷的鎖.synchronized就不是可中斷鎖,而Lock是可中斷鎖。lockInterruptibly()

?synchronized與ReentrantLock的區別?

same同點:
ReentrantLock和synchronized都是可重入鎖。
不同點:
synchronized是不可中斷鎖,而ReentrantLock則提供了中斷功能。
synchronized是非公平鎖,而ReentrantLock的默認實現是非公平鎖,但是也可以設置爲公平鎖

 

-----------ReentrantLock--------

名詞?

AQS--------------AbstractQueuedSynchronizer抽像隊列同步
CAS操作----------(CompareAndSwap)-比較和清除(巧記--比較抽象)
AQS使用一個FIFO的隊列表示排隊等待鎖的線程,包含:ReentrantLock,Semaphore,CountDownLatch,ReentrantReadWriteLock,FutureTask

CAS和AQS?(巧記--比較抽象)

一、CAS是CompareAndSwap-比較和清除, 比較舊值是否變化,變化則重新讀取再設置。,操作包含三個操作數——內存位置(V)、預期原值(A)和新值(B)。如果內存位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新爲新值。否則,處理器不做任何操作。-----CAS操作保證修改狀態碼過程的原子性

二、AQS是AbstractQueuedSynchronizer 抽象隊列同步器,裏面有3個屬性:
int鎖狀態碼--
從0變爲1表示加鎖成功;
當前加鎖線程--從Null變爲當前獲取鎖的線程;
一個等待隊列-----來存放多個等待線程。(使用volatile關鍵字保證狀態碼在線程間的可見性)
比如說ReentrantLock、ReentrantReadWriteLock等大多數併發包下的底層都是基於AQS來實現的。

1、AQSCAS的基礎上增加一個隊列來放併發拿鎖失敗的等待線程,但默認false是不公平鎖,最後進的可能先拿到鎖。可設置屬性值ture改成公平鎖,
2、從ReentrantLock的加鎖和釋放鎖的過程,給大家講清楚了其底層依賴的AQS的核心原理:

這個講一下3個線程情況,線程1加鎖和釋放鎖的過程中,線程2、3是按順序還是不按順序,這個就有公平和非公平兩種鎖的場景

基於AQS框架實現的CountDownLatch和CyclicBarrier對比:


CountDownLatch維護有個int型的狀態碼,每次調用countDown時狀態值就會減1;調用wait方法的線程會阻塞,直到狀態碼爲0時纔會繼續執行。
CyclicBarrier可以實現CountDownLatch一樣的功能,不同的是CountDownLatch屬於一次性對象,聲明後只能使用一次,而CyclicBarrier可以循環使用。
總結#
CountDownLatch創建後只能使用一次,而CyclicBarrier可以循環使用,並且CyclicBarrier功能更完善。
CountDownLatch內部的狀態是基於AQS中的狀態信息,而CyclicBarrier中的狀態值是單獨維護的,使用ReentrantLock加鎖保證併發修改狀態值的數據一致性。
它們的使用場景:允許一個或多個線程等待其他線程完成操作, 即當指定數量線程執行完某個操作再繼續執行下一個操作

 

?ReentrantLock工作原理或實現原理?

ReentrantLock是Lock的默認實現之一。使用排隊等待鎖的機制,併發安全的

 

-----------CopyOnWriteArrayList--------

CopyOnWriteArrayList是線程安全List,適合----讀多寫少時且服務器內存夠用(CopyOnWrite併發容器用於讀多寫少的併發場景。比如白名單,黑名單等場景)

?CopyOnWriteArrayList工作原理或實現原理?

寫時是需要在方法內開始時,加ReentrantLock鎖的,避免多線程寫的時候會Copy出N個副本出來。
讀的時候不需要加鎖,因爲是安全失敗機制,寫的時候會複製新的副本,讀的是舊的副本,寫完會把引用改成改好的副本。,所以改成鎖讀依然讀到的是舊對象

快速失敗fail-fast----在A線程對集合進行遍歷的同時,B線程對集合進行了刪除操作,此時會拋出併發修改異常
安全失敗機制----集合在進行內容操作的時候,會先將集合內容複製一份,在新複製的集合上進行操作。所以當線程報錯的時候不會拋出異常

?的作用或優點或應用場景?

比如ArrayList,寫的時候,有人同時在讀,就會報併發修改異常---原因是
(Iterator 遍歷開始時,會把modCount記錄下來,循環結束時會比較一下這個值是否被改了,改動過就報異常)。
1)優點:所以和ArrayList比,CopyOnWriteArrayList採用安全失敗機制,先複製副本出來改副本,改的過程中,Iterator遍歷就不會報修改異常了。
1)缺點:對於業務來說,可能數據不一致,讀的是舊值。寫的時候,因爲要複製(當一個數組裏面數據量很大時,這時在複製一個副本就容易造成GC),還有加鎖,所以很慢。但查沒鎖沒複製,所以快。

應用場景----讀多寫少時且服務器內存夠用(複製佔內存),比如白名單,黑名單

?CopyOnWriteArrayList與Vector的區別?

1、Vector是比較古老的線程安全的,但性能不行,Vector讀寫方法上都是同步的(Synchronized),
2、CopyOnWriteArrayList在兼顧了線程安全的同時,又提高了併發性,性能比Vector有不少提高----CopyOnWriteArrayList讀方法無讀;且寫方法內開始時,加ReentrantLock鎖後複製一個新副本出來記錄寫操作,之後再把引用指向新副本,釋放鎖。

?Vector & ArrayList ?

1)  Vector的讀寫方法都是同步的(Synchronized),是線程安全的,但效率低
2) 擴容不一樣,Vector會將它的容量*2,而ArrayList只增加50%的大小。

?. Hashtable & HashMap 

Hashtable和HashMap它們的性能方面的比較類似 Vector和ArrayList,比如Hashtable的方法是同步的,而HashMap的不是。

? ArrayList & LinkedList

ArrayList的內部實現是基於內部數組Object[],所以從概念上講,它更象數組,但LinkedList的內部實現是基於一組連接的記錄,所以,它更象一個鏈表結構,所以,它們在性能上有很大的差別:

?ArrayBlockingQueue、ConcurrentLinkedQueue 和 LinkedBlockingQueue 

ArrayBlockingQueue初始容量固定的阻塞隊列,我們可以用來作爲數據庫模塊成功競拍的隊列,比如有10個商品,那麼我們就設定一個10大小的數組隊列
ConcurrentLinkedQueue使用的是CAS原語無鎖隊列實現,是一個異步隊列,入隊的速度很快,出隊進行了加鎖,性能稍慢
LinkedBlockingQueue是阻塞的隊列,入隊和出隊都用了加鎖,當隊空的時候線程會暫時阻塞

Synchronized和Lock比較?

  • Synchronized是關鍵字,java內置語言實現,Lock是接口。(Java中每個對象都可以用來實現一個同步的鎖,這些鎖被稱爲內置鎖(Intrinsic Lock)或監視器鎖(Monitor Lock))----monitorenter 必須 有 對應 的 monitorexit 
  • Synchronized在線程發生異常時會自動釋放鎖,因此不會發生異常死鎖。Lock異常時不會自動釋放鎖,所以需要在finally中實現釋放鎖。
  • Lock是可以中斷鎖,Synchronized是非中斷鎖,必須等待線程執行完成釋放鎖。
  • Lock可以使用讀鎖提高多線程讀效率。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章