併發存在的問題:上下文切換耗時,死鎖,軟硬體資源限制
解決方法:減少上下文切換:
1.無鎖併發程式設計:讓不同的執行緒處理不同的資料段(將資料id採用hash演算法分配給不同的執行緒)
2.cas演算法:compare and set使用jni
3.使用最少執行緒:減少處於waiting狀態的執行緒(jstack與dump分析)
synchronize使執行緒獲得monitor,呼叫wait進入wait set
每次waiting到runnable都會進行一次上下文的切換
4.協程:單執行緒中實現多工的排程,單執行緒中多工的切換。
造成死鎖的原因:
synchronized(a)
與synchronized(b)
拿到鎖因為一些異常沒有釋放鎖
拿到乙個資料庫鎖釋放時丟擲異常
死鎖解決:避免乙個執行緒同時獲取多個鎖
乙個鎖只占用乙個資源
定時鎖:lock.trylock(timeout)替代內部鎖
資料庫鎖加減鎖必須在乙個資料庫裡
資源限制:硬體有頻寬上下行速度,硬碟速度,cpu速度
軟體有資料庫連線數,socket連線數等。
解決:硬體使用伺服器集群,hash機器
軟體:資源池復用,如使用連線池將資料庫和socket連線復用,或者在呼叫webservice介面獲取資料是只建立乙個連線
執行緒數比連線數要大時執行緒會被阻塞,等到資料庫連線。
volatile 追加到64位,避免頭尾節點互相鎖,聲言lock#,修改後寫回記憶體並使其他值無效
synchronized 物件頭儲存鎖資訊,重量級,響應緩慢
原子操作:
原子性由對匯流排加鎖和對快取加鎖來實現多處理器間的原子操作,對處理器使用lock#
匯流排鎖定會導致cpu和記憶體之間的通訊鎖住,開銷很大,因此一般使用快取鎖
automicreference類可以保證引用物件之間的原子性,把多個變數繫結在一起進行原子操作
執行緒間的通訊機制:共享記憶體和訊息傳遞
通訊:本地記憶體和主記憶體,本地記憶體重新整理至主記憶體,再從主記憶體中去讀取
編譯器優化的重排序(單執行緒程式語義)
指令集並行的重排序(多條指令的重疊執行)
記憶體系統的重排序(快取和讀寫緩衝區)
順序一致性保證所有記憶體的讀寫操作都具有原子性
匯流排事務包括讀事務和寫事務
匯流排會仲裁,同時只有乙個處理器能向匯流排發出請求
監視器會對其他處理器保持互斥
處理器(計算)->匯流排->記憶體(儲存)
volatile變數相當於對讀寫long或double的操作進行加鎖,也就是synchronized
鎖的語義決定了臨界區**的執行具有原子性
volatile具有原子性,但volatile++不具有原子性
volatile的寫相當於鎖的釋放,volatile的讀相當於鎖的獲取
volatile寫前面提到過是把本地記憶體中的共享變數值重新整理到主記憶體
讀的時候將本地記憶體置為無效,然後去主記憶體中讀取
通過新增記憶體屏障
但其實volatile只對變數有用,而鎖是互斥執行直接管理臨界區
執行緒釋放鎖就是把執行緒對應的本地記憶體中的共享變數重新整理到主記憶體中
執行緒獲得鎖的時候也是會把該執行緒對應的本地記憶體置為無效,從而使被監視器保護的臨界區**必須從主記憶體中讀取共享變數
aqs框架
獨佔鎖和共享鎖,concurrent包
獨佔鎖(悲觀鎖)模式下每次只有乙個執行緒能持有鎖,reentrantlock就是以獨佔的方式實現的互斥鎖。
共享鎖(樂觀鎖)允許多個執行緒同時獲取鎖,併發訪問共享資源,如readwritelock(一寫多讀)
aqs佇列中是存放等待執行緒
還有公平鎖和非公平鎖
cas更新同時具有volatile讀和volatile寫語義
concurrent包的實現:
1.宣告共享變數為volatile
2.使用cas的原子條件更新實現執行緒間的同步與通訊
static特性:
在裝載時初始化,不是每次建立新物件時都初始化
修飾的屬性稱為類屬性或類方法,可以直接通過類名呼叫
final特性:其值不可變,其引用不可變
延遲初始化可以降低初始化類和建立物件的開銷
通過標識位或者中斷操作的方式能夠使執行緒在終止時有機會去清理資源。
interrupted()和cancel()
執行緒開始執行時有自己的棧空間
等待通知機制:生產者和消費者通過檢測變數看是否外部環境發生改變
管道輸入輸出主要介質為記憶體,是執行緒間的資料傳輸
讀寫鎖:高16位讀,低16位寫,讀狀態s=2,寫狀態s=3
使用位運算寫狀態=s&0x0000ffff(抹去高16位)
讀狀態s>>>16
寫狀態增加1時,等於s+1,讀狀態增加1時,等於s+(1<<16),也就是s+0x00010000
寫鎖是乙個支援重進入的排它鎖。讀鎖是乙個支援重進入的共享鎖。
如果當前執行緒已經獲取了寫鎖,則增加寫狀態。
如果當前執行緒在獲取寫鎖時,讀鎖已經被獲取,則當前執行緒進入等到狀態。
只有在讀鎖都被釋放後寫鎖才能被獲取
寫鎖也會阻塞之後的所有讀寫操作
hashmap是執行緒不安全的,在併發中可能會導致死迴圈
hashtable執行緒安全但又效率低下
hashmap的put操作會導致多執行緒中,hashmap的entry鍊錶形成環形資料結構,這樣entry的next的節點永遠不為空,就會產生死迴圈獲取entry
hashtable使用了synchronized來保證執行緒安全,但激烈環境下hashtable效率非常低下,阻塞輪詢,put新增阻塞get
concurrenthashmap也就是使用了鎖分段技術,容器裡有多把鎖,每把用於鎖一部分資料,就不會產生鎖競爭
concurrentlinkedqueue使用了非阻塞迴圈cas來實現執行緒安全佇列
(需要做的事有定位尾節點,使用cas演算法將入隊節點設定成為節點的next結點,直到成功)
也就是看tail後的next
通知模式
fork/join框架:細分子任務
工作竊取演算法,使用雙端佇列
基於aqs實現的同步器包括:
reentrantlock、semaphore、reentrantreadwritelock、countdownlatch和futuretask
aqs由於使用了原子操作,因此是很多阻塞佇列的底層結構
Java併發程式設計的藝術 筆記2
現代作業系統排程的最小單位是執行緒,也叫輕量級程序,每個程序可建立多個程序,每個執行緒都有各自的計數器 堆疊和區域性變數等屬性,並且能夠訪問共享變數。處理器在這些執行緒上高速切換,讓使用者感覺到這些執行緒在同時執行。執行緒優先順序 決定執行緒分配處理器資源多少的屬性,設定時,針對頻繁阻塞 休眠或i ...
《併發程式設計的藝術》 筆記
記錄 j a 併發程式設計的藝術 部分知識點 volatile synchronized 用來闡述記憶體之間的可見性 任意乙個j a物件,都擁有一組監視器方法 定義在object上 主要包括wait nofify notifyall 等 這些方法與synchronized同步關鍵字配合可以實現等待 ...
java併發程式設計的藝術 讀書筆記 01
1.1.1 多執行緒一定快嗎?下面 並行一定比序列執行的快嗎?public class concurrencytest private static void concurrency throws interruptedexception 啟動執行緒 thread.start int b 0 for...