【java併發學習筆記2】
(一)基本概念
1.同步和非同步:
同步(Sync)
所謂同步,就是發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回或繼續執行後續操作。
簡單來說,同步就是必須一件一件事做,等前一件做完了才能做下一件事。
例如:B/S模式中的表單提交,具體過程是:客戶端提交請求->等待伺服器處理->處理完畢返回,在這個過程中客戶端(瀏覽器)不能做其他事。
非同步(Async)
非同步與同步相對,當一個非同步過程呼叫發出後,呼叫者在沒有得到結果之前,就可以繼續執行後續操作。當這個呼叫完成後,一般通過狀態、通知和回撥來通知呼叫者。對於非同步呼叫,呼叫的返回並不受呼叫者控制。
對於通知呼叫者的三種方式,具體如下:
狀態
即監聽被呼叫者的狀態(輪詢),呼叫者需要每隔一定時間檢查一次,效率會很低。
通知
當被呼叫者執行完成後,發出通知告知呼叫者,無需消耗太多效能。
回撥
與通知類似,當被呼叫者執行完成後,會呼叫呼叫者提供的回撥函式。
例如:B/S模式中的ajax請求,具體過程是:客戶端發出ajax請求->服務端處理->處理完畢執行客戶端回撥,在客戶端(瀏覽器)發出請求後,仍然可以做其他的事。
非同步操作例子:
為了避免短時間大量的資料庫操作,就使用快取機制,也就是訊息佇列。先將資料放入訊息佇列,然後再慢慢寫入資料庫。
引入訊息佇列機制,雖然可以保證使用者請求的快速響應,但是並沒有使得我資料遷移的時間變短(即80萬條資料寫入mysql需要1個小時,用了redis之後,還是需要1個小時,只是保證使用者的請求的快速響應。使用者輸入完http url請求之後,就可以把瀏覽器關閉了,幹別的去了。如果不用redis,瀏覽器不能關閉)。
總結來說,同步和非同步的區別:請求發出後,是否需要等待結果,才能繼續執行其他操作。
2.阻塞和非阻塞
阻塞和非阻塞通常用來形容多執行緒間的相互影響。比如一個執行緒佔用了臨界區資源,那麼其它所有需要這個資源的執行緒就必須在這個臨界區中進行等待,等待會導致執行緒掛起。這種情況就是阻塞。此時,如果佔用資源的執行緒一直不願意釋放資源,那麼其它所有阻塞在這個臨界區上的執行緒都不能工作。函式只有在得到結果之後才會返回。有人也許會把阻塞呼叫和同步呼叫等同起來,實際上它們是不同的。對於同步呼叫來說,很多時候當前執行緒還是啟用的,只是從邏輯上當前函式沒有返回而已。
非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前,該函式不會阻塞當前執行緒,而會立刻返回。非阻塞允許多個執行緒同時進入臨界區
3.飢餓、死鎖和活鎖
飢餓指一個或者多個執行緒由於種種原因一直無法獲得所需要資源,導致一直無法執行。
死鎖表示多個執行緒互相等到獲取其他執行緒的鎖才能完成操作釋放本身佔有的鎖,導致大家都無法獲取鎖完成操作。
活鎖指的是任務或者執行者沒有被阻塞,由於某些條件沒有滿足,導致一直重複嘗試,失敗,嘗試,失敗。 活鎖和死鎖的區別在於,處於活鎖的實體是在不斷的改變狀態,所謂的“活”, 而處於死鎖的實體表現為等待;活鎖有可能自行解開,死鎖則不能。
單一實體的活鎖
例如執行緒從佇列中拿出一個任務來執行,如果任務執行失敗,那麼將任務重新加入佇列,繼續執行。假設任務總是執行失敗,或者某種依賴的條件總是不滿足,那麼執行緒一直在繁忙卻沒有任何結果。
協同導致的活鎖
生活中的典型例子: 兩個人在窄路相遇,同時向一個方向避讓,然後又向另一個方向避讓,如此反覆。
通訊中也有類似的例子,多個使用者共享通道(最簡單的例子是大家都用對講機),同一時刻只能有一方傳送資訊。傳送訊號的使用者會進行衝突檢測, 如果發生衝突,就選擇避讓,然後再發送。 假設避讓演算法不合理,就導致每次傳送,都衝突,避讓後再發送,還是衝突。
計算機中的例子:兩個執行緒發生了某些條件的碰撞後重新執行,那麼如果再次嘗試後依然發生了碰撞,長此下去就有可能發生活鎖。
解決協同活鎖的一種方案是調整重試機制。
比如引入一些隨機性。例如如果檢測到衝突,那麼就暫停隨機的一定時間進行重試。這回大大減少碰撞的可能性。 典型的例子是乙太網的CSMA/CD檢測機制。
另外為了避免可能的死鎖,適當加入一定的重試次數也是有效的解決辦法。儘管這在業務上會引起一些複雜的邏輯處理。
比如約定重試機制避免再次衝突。 例如自動駕駛的防碰撞系統(假想的例子),可以根據序列號約定檢測到相撞風險時,序列號小的飛機朝上飛, 序列號大的飛機朝下飛。
死鎖與活鎖的區別:
死鎖:迎面開來的汽車A和汽車B過馬路,汽車A得到了半條路的資源(滿足死鎖發生條件1:資源訪問是排他性的,我佔了路你就不能上來,除非你爬我頭上去),汽車B佔了汽車A的另外半條路的資源,A想過去必須請求另一半被B佔用的道路(死鎖發生條件2:必須整條車身的空間才能開過去,我已經佔了一半,尼瑪另一半的路被B佔用了),B若想過去也必須等待A讓路,A是輛蘭博基尼,B是開奇瑞QQ的屌絲,A素質比較低開窗對B狂罵:快給老子讓開,B很生氣,老子就不讓(死鎖發生條件3:在未使用完資源前,不能被其他執行緒剝奪),於是兩者相互僵持一個都走不了(死鎖發生條件4:環路等待條件),而且導致整條道上的後續車輛也走不了。
活鎖:馬路中間有條小橋,只能容納一輛車經過,橋兩頭開來兩輛車A和B,A比較禮貌,示意B先過,B也比較禮貌,示意A先過,結果兩人一直謙讓誰也過不去。
4.併發級別:
分別為阻塞和非阻塞,其中非阻塞又分為無障礙、無鎖、無等待。
無障礙是最弱的非組賽排程,執行緒可以自由進入臨界區,無競爭時,有限步內完成操作推出臨界區;有競爭時回滾資料。是一種寬進嚴出的排程,不保證有執行緒可以出來。執行緒進入臨界區容易,出來難。
無鎖是在無障礙地基礎上,要保證有一個執行緒可以出臨界區。
例如:
while (!atomicVar.compareAndSet(localVar, localVar+1))
{
localVar = atomicVar.get();
}
無等待在無鎖的基礎上要求所有的執行緒必須在有限步內完成操作退出臨界區,所有執行緒是無飢餓的。
5.有關執行緒的兩個zh重要定律
(1)Amdahl定律
定義了序列系統並行化後的加速比的計算公式和理論上限
加速比定義:加速比=優化前系統耗時/優化後系統耗時
說明增加CPU處理器的數量並不一定能起到有效的作用,提高系統內可並行化的模組比重,合理增加並行處理器數量,才能以最小的投入,得到最大的加速比
(2)Gustafson定律
說明處理器個數,序列比例和加速比之間的關係
(二)、執行緒基礎
1、執行緒的狀態
2.幾個方法的比較
Thread.sleep(long millis),一定是當前執行緒呼叫此方法,當前執行緒進入TIMED_WAITING狀態,但不釋放物件鎖,millis後執行緒自動甦醒進入就緒狀態。作用:給其它執行緒執行機會的最佳方式。
Thread.yield(),一定是當前執行緒呼叫此方法,當前執行緒放棄獲取的CPU時間片,但不釋放鎖資源,由執行狀態變為就緒狀態,讓OS再次選擇執行緒。作用:讓相同優先順序的執行緒輪流執行,但並不保證一定會輪流執行。實際中無法保證yield()達到讓步目的,因為讓步的執行緒還有可能被執行緒排程程式再次選中。Thread.yield()不會導致阻塞。該方法與sleep()類似,只是不能由使用者指定暫停多長時間。
t.join()/t.join(long millis),當前執行緒裡呼叫其它執行緒t的join方法,當前執行緒進入WAITING/TIMED_WAITING狀態,當前執行緒不會釋放已經持有的物件鎖。執行緒t執行完畢或者millis時間到,當前執行緒進入就緒狀態。
obj.wait(),當前執行緒呼叫物件的wait()方法,當前執行緒釋放物件鎖,進入等待佇列。依靠notify()/notifyAll()喚醒或者wait(long timeout) timeout時間到自動喚醒。
obj.notify()喚醒在此物件監視器上等待的單個執行緒,選擇是任意性的。notifyAll()喚醒在此物件監視器上等待的所有執行緒。
3.中斷執行緒:
public void Thread.interrupt() // 中斷執行緒,只是將當前執行緒的中斷標記設定為true,但是不保證虛擬機器一定會中斷當前執行緒
如下面這段程式碼,執行緒可能永遠不會中斷:
public boolean Thread.isInterrupted() // 判斷是否被中斷,利用此方法可以讓執行緒優雅的中斷
public static boolean Thread.interrupted() // 判斷是否被中斷,並清除當前中斷狀態
如果執行緒在執行時sleep,如果在sleep時被中斷,將會丟擲InterruptedException異常並清除中斷標記位。因此在捕獲到異常後要自己將執行緒的中斷標記標記為true,否則執行緒可能不會結束
4.守護執行緒
在後臺默默地完成一些系統性的服務,比如垃圾回收執行緒、 JIT執行緒就可以理解為守護執行緒。當一個Java應用內,只有守護執行緒時,Java虛擬機器就會自然退出
Thread t=new DaemonT();
t.setDaemon(true);
t.start();
5.執行緒優先順序
高優先順序的執行緒更容易再競爭中獲勝
Thread high=new HightPriority();
LowPriority low=new LowPriority();
high.setPriority(Thread.MAX_PRIORITY);
low.setPriority(Thread.MIN_PRIORITY);
low.start();
high.start();
synchronized
– 指定加鎖物件:對給定物件加鎖,進入同步程式碼前要獲得給定物件的鎖。
– 直接作用於例項方法:相當於對當前例項加鎖,進入同步程式碼前要獲得當前例項的鎖。
– 直接作用於靜態方法:相當於對當前類加鎖,進入同步程式碼前要獲得當前類的鎖。
6.指令執行的原子性、有序性和可見性
這篇文章寫得很好:
相關推薦
【java併發學習筆記2】
(一)基本概念 1.同步和非同步: 同步(Sync) 所謂同步,就是發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回或繼續執行後續操作。 簡單來說,同步就是必須一件一件事做,等前一件做完了才能做下一件事。 例如:B/S模式中的表單提交,具體過程是:客戶端提交請
【Python爬蟲學習筆記2】urllib庫的基本使用
代理服務 cor proc 技術 origin car windows tpc -c urllib庫是python內置的實現HTTP請求的基本庫,通過它可以模擬瀏覽器的行為,向指定的服務器發送一個請求,並保存服務器返回的數據。 urlopen函數 函數原型:urlopen(
【Python學習筆記2】turtle庫繪相簿使用
5.in[‘C’,’c’]保留字,二元關係操作,符合右側即為真, 6.print(“這裡輸入文字:%.2fF”%f)表示二位小數的浮點數,%f表示輸出的是f的值。 7.迴圈 for i in range (10): 執行 8.
【燕十八PHP學習筆記-2】運算
2014-8-25 常量命名規則和變數一樣,語法上允許小寫,但習慣大寫。 常量也區分大小寫。 如果引用了一個未曾定義的常量,因為沒定義該常量,自然找不到值,最終會把常量名當成字串來輸出。 2014-8-29 算數運算一般格式測試: echo $a / $b,'<br />' 如果在C語言中,$a、
【 C/C++學習筆記整理】--2.break與return0、常用函式的用法
5.break和return 0 的區別 break 是跳出迴圈,執行迴圈體的外的程式;return 0 是結束程式,返回到main函式 6.sort()函式的用法 sort(begin,end,cmp),cmp引數可以沒有,如
【Java併發學習四】如何實現一個定時執行緒池
所謂 “定時任務執行緒池” 就是指放入執行緒池的任務,可以按照指定的等待週期迴圈執行。 Java裡面ScheduledThreadPoolExecutor這個類實現了這種功能。Spring裡面的定時任務也是在ScheduledThreadPoolExecu
【Java併發學習五】圖解ThreadLocal
簡單整理下ThreadLocal的原理,以及它需要注意的記憶體洩漏。 ThreadLocal原理 ThreadLocal不多介紹,可看作執行緒內的區域性變數(這個比喻很貼切)。我們平時宣告的區域性變數的範圍一般是方法內的,而ThreadLocal變數的範
【LINUX C學習筆記 3】管道通訊2-標準流管道
從檔案結構體指標stream中讀取資料,每次讀取一行。讀取的資料儲存在buf指向的字元陣列中,每次最多讀取bufsize-1個字元(第bufsize個字元賦'\0'),如果檔案中的該行,不足bufsize個字元,則讀完該行就結束。如若該行(包括最後一個換行符)的字元數超過bufsize-1,則fgets只返
【Python爬蟲學習筆記10】多線程中的生產者消費者模式
其中 因此 問題 共享 and 生產者消費者模式 共享問題 由於 接下來 在多線程編程中,最經典的模式是生產者消費者模式。其中,生產者是專門用來生產數據的線程,它把數據存放在一個中間變量中;而消費者則從這個中間變量取出數據進行消費。由於生產者和消費者共享中間變量,這些變量大
【java自定義註解2】java自定義註解結合Spring AOP
承接上一篇,註解應用於屬性,本篇定義了一個用於方法的註解,結合Spring AOP 實現 切面程式設計。 以下demo演示使用了SpringBoot,與SSM中使用方式大致相同,效果如下: 1、自定義註解(用
Java 8 學習筆記2——通過行為引數化傳遞程式碼
行為引數化就是可以幫助你處理頻繁變更的需求的一種軟體開發模式。一言以蔽之,它意味著拿出一個程式碼塊,把它準備好卻不去執行它。這個程式碼塊以後可以被你程式的其他部分呼叫,這意味著你可以推遲這塊程式碼的執行。例如,你可以將程式碼塊作為引數傳遞給另一個方法,稍後再去執行它。這樣,這個方法的行為就基
Java併發學習筆記
一、程序 執行緒 程序:一個程序來對應一個程式, 每個程序對應一定的記憶體地址空間,並且只能使用它自己的記憶體空間,各個程序間互不干擾。 程序儲存了程式每個時刻的執行狀態,這樣就為程序切換提供了可能。當程序暫停時,它會儲存當前程序的狀態(比如程序標識、程序的使用的資源等),在下一次重新切換回來時,便根據之前儲
【Robot定位 學習筆記 1】GPS和IMU(慣導)在無人駕駛中的應用
無人駕駛定位技術 行車定位是無人駕駛最核心的技術之一,全球定位系統(GPS)在無人駕駛定位中也擔負起相當重要的職責。然而無人車是在複雜的動態環境中行駛,尤其在大城市,GPS多路徑反射的問題會很明顯。這樣得到的GPS定位資訊很容易就有幾米的誤差。對於在有限寬度高速行駛的汽車來說,這樣的誤差很有可能
【 C/C++學習筆記整理】--3.取陣列中的其中一位,將其中幾位組合起來
10.定義一個數組,取陣列中的其中幾位,將其中幾位組合起來 const int f[10]={6,2,5,5,4,5,6,3,7,6}; int match(int num) { int k=0; for(int i=n
【 C/C++學習筆記整理】--1.常量的用法、指標與陣列的區別
巨集常量與const常量的區別: 指標與陣列的區別: ++i和i++的區別: 求X的n次冪 pow(X,n); 1.巨集常量與const常量的區別: 巨集常量,如 #define MAX_NUM 65536 本質為字
Java併發學習筆記(二)-Executor捕獲異常機制
學習《java程式設計思想》的Executor捕獲異常的時候,發現程式碼輸出跟書上有出入,於是就研究了一下Executor的機制。 (一)異常捕獲例項 1、異常處理類MyUncaughtExceptionHandler public class MyUncaughtExc
Java併發學習筆記(八)-LinkedBlockingQueue
LinkedBlockingQueue是由連結串列組成的阻塞佇列,先來看demo public class LinkedBlockingQueueDemo { public static void main(String[] args) { ExecutorServ
Java併發學習筆記(四)-柵欄CyclicBarrier
閉鎖是一次性物件,一旦進入終止狀態,就不能被重置,它是用來啟動一組相關的操作,或者等待一組相關的操作結束。 柵欄跟閉鎖有點類似,它能阻塞一組執行緒直到某個時間發生,但是這裡有個很大的區別,在柵欄裡,只有這組執行緒都到達柵欄位置時,才能繼續執行 public class C
Java併發學習筆記(九)-原子類AtomicInteger
AtomicInteger能夠保證對一個整型的操作是原子性。像i++這個操作不是原子操作,存在競態條件,所以需要加鎖,但是加鎖的效能不高,如果僅僅為了對一個整數加1。我們來看下他的實現。 private volatile int value; AtomicInte
【Linux核心學習筆記四】記憶體管理-夥伴系統
1.夥伴系統演算法描述 linux系統採用夥伴系統演算法來解決外碎片問題。主要做法是記錄現存的空閒連續頁框塊的情況,以儘量避免為滿足對小塊的請求而分割大的空閒塊。 夥伴系統演算法中,把所有的空閒頁框分為11個組,每個組對應一個連結串列,每個連結串列分