java並發編程(四)
- 2020 年 3 月 11 日
- 筆記
根據課程大綱整理資料。

21:volatile關鍵字的可見性
volatile 解決的是多核CPU帶來的快取與CPU之間數據的可見性,實現禁止指令重排

volatile即記憶體屏障,可保證一段記憶體中一個變數的原子性,原生類型都是原子性的。所以java中 volatile long,volatile double都是執行緒安全的。
22:volatile與static的關係
volatile 是保證從主記憶體載入到執行緒工作記憶體的值是最新的,此時並未載入並刷新到主記憶體中,static表示當前類的實例在主記憶體中用的是同一份。只能保證變數的來源,volatile, 聲明變數值的一致性;static,聲明變數的唯一性。
23:Atomic的原子性
原生類型都是原子性,以AtomicInteger為例
{@link java.util.concurrent.atomic} package specification for * description of the properties of atomic variablesgetAndIncrement()方法會原子性的進行增量操作把當前值加1。Atomic要跟CAS放在一起
public final int getAndAdd(int delta) { return unsafe.getAndAddInt(this, valueOffset, delta); }24:CAS Compare and Swap
private static final Unsafe unsafe
private volatile int value;
unsafe類是CAS的核心類,是由C語言native方法來訪問的
public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }其中getAndAddInt方法,this表示當前對象valueoffset表示記憶體中的偏移地址,delta是當前value增加的變數
CAS即比較當前值與預設值,交換並增加,如果與預想一致就交換,否則再次自旋,所以帶來循環開銷問題,進而引來ABA問題。
25:Threadlocal
這個類提供執行緒局部變數。 這些變數與其正常的對應方式不同,因為訪問一個的每個執行緒(通過其get或set方法)都有自己獨立初始化的變數副本。 ThreadLocal實例通常是希望將狀態與執行緒關聯的類中的私有靜態欄位(例如,用戶ID或事務ID)
1.如果多個執行緒訪問同一個共享Threadlocal變數,是保證執行緒隔離的,A,B,C訪問的即Threadlocal的變數副本
2.與執行緒狀態相關,單個執行緒內,該執行緒持有該資源變數是私有的
Threadlocal並不是為了保證執行緒安全,而是為了在執行緒內方便共享變數跨方法的傳遞
經典用法:@Transactional事務的處理,利用其get方法獲取Entry[],為每一個執行緒提供變數的副本,實現執行緒的隔離,每個執行緒只訪問自己的數據。
26:InheritableThreadLocal與Threadlocal
首先InheritableThreadLocal繼承了Threadlocal,Inheritable本身即是繼承的意思,
/** * This class extends <tt>ThreadLocal</tt> to provide inheritance of values * from parent thread to child thread: when a child thread is created, the * child receives initial values for all inheritable thread-local variables * for which the parent has values. Normally the child's values will be * identical to the parent's; however, the child's value can be made an * arbitrary function of the parent's by overriding the <tt>childValue</tt> * method in this class. * * <p>Inheritable thread-local variables are used in preference to * ordinary thread-local variables when the per-thread-attribute being * maintained in the variable (e.g., User ID, Transaction ID) must be * automatically transmitted to any child threads that are created. * * @author Josh Bloch and Doug Lea * @see ThreadLocal * @since 1.2 */ public class InheritableThreadLocal<T> extends ThreadLocal<T> { ... }創建子執行緒時,子執行緒接收所有可繼承執行緒局部變數的初始值。
說實話第一次聽到跟他比較,比如ThreadLocal VS FastThreadlocal,既然是繼承,大多是為了擴展父類方法,解決其threadlocal.get()得不到其新執行緒中的值,
推薦部落格: https://blog.csdn.net/ni357103403/article/details/51970748
27:Unsafe類詳解
Unsafe類除了CAS理解相關以外,確實了解的不多,這裡推薦美團技術部落格
Java魔法類:Unsafe應用解析 https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html
它屬於native.C語言編寫的類,在cas,netty中都有應用。這裡提到了執行緒調度可用於執行緒掛起,真實調用了park()方法實現。
28:同步類容容器
CopyOnWriteArraySet
ConcurentSkipListSet
ConcurrentHashMap
與 ConcurrentSkipListMap
ConcurrentHashMap 加鎖
ConcurrentSkipListMap 不需要加鎖,浪費空間
29:ConcurrentHashMap
Hashmap是並發執行緒不安全的,其無法解決共享變數的安全問題,那麼解決的方法為
1.替換為Hashtable
2.Collections.synchronizedMap()
3.ConcurrentHashMap
其中Hashtable之所以安全因為其put,get等大多數方法為synchronized
修飾的,但並發場景下鎖的粒度太重,性能降低
Collections.synchronizedMap()
public static void main(String[] args) { HashMap<String, String> map = new HashMap<>(); Map<String, String> synchronizedMap = Collections.synchronizedMap(map); }其put方法get方法也都是synchronized
public V put(K key, V value) { synchronized (mutex) {return m.put(key, value);} } public V get(Object key) { synchronized (mutex) {return m.get(key);} }ConcurrentHashMap
1.7是鎖分段 segment 16,隔離級別太大,有很多空間就浪費了,太小就段內的元素過多
1.8以後是cas演算法C語言寫得,無鎖演算法,put添加的時候,鏈表+紅黑樹
put方法(無鎖添加)

29:無阻塞隊列ConcurrentLinkedQueue
An unbounded thread-safe {@linkplain Queue queue} based on linked nodes. * This queue orders elements FIFO (first-in-first-out). public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> implements Queue<E>, java.io.Serializable { }一個為綁定執行緒安全的隊列,隊列的元素先進先出
看其節點的內部方法
/** * Constructs a new node. Uses relaxed write because item can * only be seen after publication via casNext. */ Node(E item) { UNSAFE.putObject(this, itemOffset, item); } 構造新節點。使用輕鬆寫入,因為item可以僅在通過casNext發布後才能看到那其實還是跟Unsafe類離不開。
推薦部落格http://ifeve.com/concurrentlinkedqueue/
我沒有夢見你, 但我夢見了糖葫蘆, 醒來我突然覺得我好久沒吃過糖葫蘆了 然後就想跟你講一下 可能 你和糖葫蘆一樣甜吧


