java併發編程筆記(一)——簡介
線程不安全的類示例java
public class CountExample1 {
// 請求總數
public static int clientTotal = 5000;
// 同時併發執行的線程數
public static int threadTotal = 200;
public static int count = 0;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count); //最終獲得的結果是小於5000的
}
private static void add() {
count++;
}
}
併發:編程
同時擁有兩個或者多個線程,若是程序在單核處理器上運行,多個線程將交替的換入換出內存,這些線程是同時「存在」的,每一個線程都處於執行過程當中的某個狀態,若是運行在多核處理器上,程序中的每一個線程都將分配到一個處理器核上,所以能夠同時運行。緩存
多個線程操做相同的資源,保證線程安全,合理使用資源安全
高併發(High Concurrency):架構
高併發是互聯網分佈式系統架構設計中必須考慮的因素之一,它一般是指,經過設計保證系統可以同時並行處理不少請求。併發
java 內存模型(Java Memory Model,JMM)
java堆分佈式
- 能夠在運行時動態的分配內存大小;
- 因爲在運行時動態的分配大小,因此他的存取速度很慢;
java棧高併發
- 存取速度很快,僅次於計算機的寄存器,棧裏的數據都是能夠共享的;
- 棧因爲存放的數據大小與生存期是要求肯定的,因此缺乏必定的靈活性;
java線程引用對象的圖示優化
![](http://static.javashuo.com/static/loading.gif)
cpu訪問主存圖示ui
![](http://static.javashuo.com/static/loading.gif)
當兩個線程同時訪問一個對象時,這兩個線程持有的是這個對象的私有拷貝。
cpu模型圖
![](http://static.javashuo.com/static/loading.gif)
- cpu訪問寄存器的速度遠大於在主存上的讀取速度
- 因爲計算機的存儲設備與處理器的處理速度有好幾個數據量級的差距,因此存在了高速緩存區
java內存模型抽象結構圖
![](http://static.javashuo.com/static/loading.gif)
java內存模型-同步八種操做
- lock(鎖定):做用於主內存的變量,把一個變量標識爲一條線程獨佔狀態
- unlock(解鎖):做用於主內存的變量,把一個處於鎖定狀態的變量釋放出來,釋放後的變量才能夠被其餘線程鎖定
- read(讀取):做用主內存的變量,把一個變量值由主內存傳輸到線程的工做內存中,以便隨後的load使用
- load(載入):做用於工做內存的變量,它把read操做從主內存中獲得的變量值放入工做內存的變量副本中
- use(使用):做用於工做內存的變量,把工做內存中的一個變量值傳遞給執行引擎
- assign(賦值):做用於工做內存的變量,它把一個從執行引擎接收到的值賦值給工做內存的變量
- store(存儲):做用於工做內存的變量,把工做內存中的一個變量的值傳送到主存中,以便隨後的write的操做
- write(寫入):做用於主內存的變量,它把store操做從工做內存中一個變量的值傳送到主內存的變量中
java內存模型-同步規則
- 若是要把一個變量從主存中複製到工做內存,就須要按順序的執行read和load操做,若是把變量從工做內存中同步回主內存中,就要按順序的執行store和write操做。但java內存模型只要求上述操做必須按順序執行,而沒有保證必須是連續執行
- 不容許read和load、store和write操做之一單獨出現
- 不容許一個線程丟棄它的最近assign的操做,即變量在工做內存中改變了以後,必須同步到主內存中
- 不容許一個線程無緣由的(沒有發生過任何assign操做)把數據從工做內存同步回主內存中
- 一個新的變量只能在主內存中誕生,不容許在工做內存中直接使用一個未被初始化(load和assign)的變量。即就是對一個變量實施use和store操做以前,必須先執行過了assign和load操做
- 一個變量在同一時刻只容許一條線程對其進行lock操做,但lock操做能夠被同一條線程重複執行屢次,屢次執行lock後,只有執行相同次數的unlock操做,變量纔會被解鎖,lock和unlock必須成對出現
- 若是對一個變量執行lock操做,將會清空工做內存中此變量的值,在執行引擎使用這個變量前須要從新執行load或assign操做初始化變量的值
- 若是一個變量事先沒有被lock操做鎖定,則不容許對它執行unlock操做,也不容許去unlock一個被其餘線程鎖定的變量
- 對一個變量執行unlock操做以前,必須先把此變量同步到主內存中(執行store和write操做)
併發的優點和風險
優點
- 同時處理多個請求,響應更快,負責的操做能夠分紅多個進程同時進行
- 程序設計在某些狀況下更簡單,也能夠有更多的選擇
- CPU可以在等待IO的時候作一些其餘的事情
風險
- 多個線程共享數據可能會產生與指望不相符的結果
- 某個操做沒法繼續進行下去時,就會發生活躍性問題,好比死鎖、飢餓等問題
- 線程過多時會使得CPU頻繁切換,調度時間增多,同步至機制;消耗過多內存
總結
- CPU多級緩存:緩存一致性,亂序執行優化
- java內存模型:JMM規定、抽象結構、同步八中操做及規則
- java併發的優點與風險