1. 程式人生 > >關於JAVA高併發相關學習

關於JAVA高併發相關學習

參考書籍:《實戰JAVA高併發程式設計》本文僅用於自己參考
一、概念

  • 同步(Synchronous)和非同步(Asynchronous)
    在這裡插入圖片描述
  • 併發(Concurrency)和並行(Parallelism)
    在這裡插入圖片描述
  • 臨界區
    臨界區用來表示一種公共資源或者說是共享資料,可以被多個執行緒使用。但是每一次,只能有一個執行緒使用它,一旦臨界區資源被佔用,其他執行緒想要使用這個資源就必須等待。
  • 阻塞(Blocking)和非阻塞(Non-Blocking)
    在這裡插入圖片描述
  • 死鎖(DeadLock)、飢餓(Starvation)和活鎖(Livelock)
    死鎖就是多個執行緒之間,相互佔用對方需要的資源而都不進行釋放,導致彼此之間相互等待對方釋放資源,產生無限制等待的現象。
    在這裡插入圖片描述

二、Java並行程式基礎
1、執行緒就是輕量級的程序,是程式執行的最小單位,使用多執行緒而不是用多執行緒去進行兵法程式的設計,是因為執行緒間的切換和排程的成本遠遠小於程序。
在這裡插入圖片描述

2、wait()和notify()的工作流程細節
在這裡插入圖片描述

3、volatile與java記憶體模型
java記憶體模型具有原子性、有序性、可見性。
通過關鍵字volatile來修飾的變數為共享變數,有不斷被修改的風險。

4、synchronized
關鍵字synchronized的作用是實現執行緒間的同步,他的工作是對同步的程式碼加鎖,使得每一次只能有一個執行緒進入同步塊,從而保證執行緒間的安全性。
關鍵字sychronized的用法

  • 指定加鎖物件:對給定物件加鎖,進入同步程式碼前要獲得給定物件的鎖
  • 直接作用於例項方法:相當於對當前例項加鎖,進入同步程式碼前要獲得當前例項的鎖
  • 直接作用於靜態方法:相當於對當前類加鎖,進入同步程式碼前要獲取當前類的鎖

5、ArrayList與Vector
多執行緒情況下,ArrayList會導致程式出錯,因為ArrayList是一個執行緒不安全的容器
使用Vecotr代替ArrayList即可

6、HashMap與ConcurrentHashMap
HashMap同樣不是執行緒安全的
使用ConcurrentHashMap代替HashMap

7、加鎖問題
多執行緒間,對於在變的物件加鎖,會導致對臨界區程式碼控制出現問題。
synchronized加鎖在Integer等變數上,會產生詭異問題,原因是Integer不能改變值,參考valueOf等方法,都是通過new Integer()返回的,所以針對加鎖問題,應該加在同一個物件上。

8、執行緒複用:執行緒池
執行緒池中提供一定數量的執行緒,外部呼叫需要建立線城時,從執行緒池中獲取空閒的執行緒;當外部使用執行緒完成後需要關閉執行緒時,只需要把執行緒放回執行緒池。JDK提供了一套Executor框架對執行緒進行有效的控制。java.util.concurrent包中,是JDK併發包的核心類

三、JDK併發包
java.util.concurrent包中
ConcurrentHashMap:這是一個高效的併發HashMap,是一個執行緒安全的HashMap;
CopyOnWriteArrayList:這是一個List,讀多寫少的場合,這個List效能非常好,遠遠好於Vector;
ConcurrentLinkedQueue:高效的併發佇列,使用連結串列實現,可以看做是執行緒安全的LinkedList;
BlockingQueue:這是一個介面,JDK內部通過連結串列、陣列等方式實現了這個介面,標識阻塞佇列,非常適合用於作為資料共享的通道;
ConcurrentSkipListMap:跳錶的實現,這是一個map,使用跳錶的資料結構進行快速查詢。
1、HashMap
如果需要一個執行緒安全的HashMap,可以通過Collections.synchronizedMap()方法實現,產生一個名為SynchronizedMap的map,它使用託管,將自己所有Map相關的功能交給傳入的hashMap實現,而自己則主要負責保證執行緒安全。
public static Map map = Collections.synchronizedMap(new HashMap());

一個更加專業的併發HashMap是ConcurrentHashMap,專門為併發進行了效能優化,跟適合多執行緒場景。

2、List
ArrayList 和Vector都是使用陣列作為其內部實現,不同的是,Vector是執行緒安全的。
LinkedList使用連結串列的資料結構實現了List,但也不是執行緒安全的,但可以通過Collections.synchonizedList()方式實現執行緒安全。

3、ConcurrentLinkedQueue
佇列Queue也是常用的資料結構之一,ConcurrentLinkedQueue是用來實現高併發的佇列,使用連結串列資料結構。

4、CopyOnWriteArrayList
很多場景中,讀操作可能會遠遠大於寫操作,讀操作是安全的,但遇見寫-寫操作或者讀-寫操作時,併發量大的情況下就會有不一致的情況產生。CopyOnWriteArrayList類讀取是完全不加鎖的,寫入也不會阻塞讀取操作,只有寫入-寫入之間需要同步等待。因為在寫入操作時,會進行一次自我複製。寫完之後,再將複製替換原來的資料,其他執行緒讀取會立刻察覺,因為array變數是volatile型別。

四、並行模式與演算法
1、單例模式:一種建立物件的模式,用於產生一個物件的具體例項。確保系統中一個類只產生一個例項。
好處1:對於頻繁的使用物件,可以省略new操作花費的時間,這對於重量級物件而言,節省了一筆很可觀的開銷。
好處2:由於new操作的減少,對系統記憶體的使用頻率也會降低,這將減輕GC的壓力,縮短GC停頓時間。

不變模型:類似String類、基礎型別的封裝型別,使用final關鍵字對類進行修飾
final修飾屬性,確保屬性只有一次賦值後就永遠不會發生改變;
對class進行修飾,確保了類不會有子類。

2、生產者-消費者模式
核心是共享記憶體快取區,是生產者和消費者之間的橋樑,避免兩者直接通訊,對兩者進行解耦。

3、演算法:
搜尋:二分法
排序:氣泡排序(冒泡迭代排序、奇偶交換排序)、希爾排序 import java.util.concurrent.CountDownLatch;
public class bubbleSort {

//方法一
public static int[] bubbleSort(int[] array) {
    Long begin = System.currentTimeMillis();
    for (int i = array.length - 1; i > 0; i--) {
        for (int j = 0; j < i; j++) {
            if (array[j] > array[j + 1]) {
                int temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
    System.out.println(System.currentTimeMillis() - begin);
    return array;
}

//方法二:【序列】奇偶交換排序exchFlag:記錄當前迭代是否發生了資料交換;start記錄是奇交換還是偶交換
public static int[] oddEventSort(int[] arr) {
    Long begin = System.currentTimeMillis();
    int exchFlag = 1, start = 0;
    while (exchFlag == 1 || start == 1) {
        exchFlag = 0;
        for (int i = start; i < arr.length - 1; i += 2) {
            if (arr[i] > arr[i + 1]) {
                int temp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = temp;
                exchFlag = 1;
            }
        }
        if (start == 0) {
            start = 1;
        } else {
            start = 0;
        }

    }
    System.out.println(System.currentTimeMillis() - begin);

    return arr;
}

//希爾排序
public static int[] insertSort(int[] arr){
    int length = arr.length ;
    int i , j , key ;
    for(i = 1 ; i < length ; i++){
       key = arr[i];
       j = i-1 ;
       while (j >= 0 && arr[j] > key){
           arr[j + 1] = arr[j];
           j-- ;
       }
       arr[j+1] = key ;
    }
    return arr ;
}

}