1.1 常見的鎖策略
-
預測鎖衝突概率
-
樂觀鎖:加鎖的時候,假設出現鎖衝突的概率不大。圍繞加鎖做的工作會更少。
-
悲觀鎖:加鎖的時候,假設鎖出現衝突的概率很大。圍繞加鎖做的工作會更多。
-
synchronized “自適應” 初始是樂觀的。鎖衝突達到一定程度就會轉變爲悲觀的。
-
加鎖開銷(時間開銷)
-
重量級鎖
-
輕量級鎖
-
挂起等待鎖:悲觀鎖/重量級鎖的一種典型實現。讓出cpu資源,過一段時間通過其他途徑得知,再伺機而動。
-
自旋鎖:樂觀鎖/輕量級鎖的一種典型實現。忙等,等待過程中不釋放cpu資源,反復檢測鎖是否被釋放。一旦釋放立即有機會獲取到鎖。
-
公平鎖:先來後到
-
非公平鎖:synchronized ,剩下的都公平競爭
-
可重入鎖 1)記錄當前是哪個綫程使用這個鎖 2)在加鎖是判定,申請當時鎖的綫程是否就是鎖的持有者綫程 3)計數器,記錄加鎖次數,從而確定何時真正釋放鎖
-
不可重入鎖
-
死鎖問題:針對一把鎖連續兩次加鎖就可能出現死鎖,可以把鎖設置成“可重入鎖”
-
讀寫鎖
-
synchronized并非是讀寫鎖
-
把加鎖操作分爲“讀加鎖”和”寫加鎖“,提供了兩種加鎖api,解鎖的api相同。
-
如果,多个线程,同时读这个变量,没有线程安全问题。但是,一个线程读/一个线程写或者两个线程都写就会产生问题,
-
如果两个线程,都是按照读方式加锁,此时不会产生锁冲突。如果两个线程,都是加写锁,此时会产生锁冲突。如果一个线程读锁,一个是写锁,也会产生锁冲突。
-
系統内置鎖,可重入讀寫鎖ReentrantReadWriteLock。内部類ReentrantReadwriteLock.ReadLock/ReentrantReadWriteLock.WriteLock。lock/unlock方法。
1.2 synchronized原理
-
乐观悲观自适应
-
重量轻量,自适应
-
自旋挂起等待,自适应
-
非公平锁
-
可重入锁
-
不是读写锁
-
鎖升級:刚开始使用synchronized加锁,首先锁会处于“偏向锁”状态。遇到线程之间的锁竞争,升级到“轻量级锁“。进一步的统计竞争出现的频次,达到一定程度之后,升级到“重量級锁”。
-
synchronized加锁的时候,会经历无锁=>偏向锁=>轻量级锁=>重量级锁。出現競爭/競爭激烈。鎖升級對當前jvm來説不可逆。
-
偏向鎖不是真鎖,只是做個標記,比較輕量高效。
-
鎖消除。(編譯器優化策略)
-
鎖粗化。(編譯器優化策略)。鎖的粒度。synchronized{}裏代碼越多,粒度越粗。把多個”細粒度“的鎖合并成”粗粒度“的鎖。
1.3 CAS
-
compare and swap
-
比较内存和cpu寄存器中的内容.如果发现相同,就进行交换(交换的是内存和另一个寄存器的内容)
-
比较内存和寄存器1中的值,是否相等如果不相等,就无事发生。如果相等,就交换内存和寄存器2的值
-
此处一般只是关心,内存交换后的内容。不关心寄存器2交换后的内容。相當於”賦值“
-
一個cpu指令就能完成。可以寫”無鎖化編程“。
-
使用場景:
-
基於CAS實現”原子類“。對int/long等類型進行封裝,從而可以原子地完成++等操作。標準庫裏也有。
package thread; import java.util.concurrent.atomic.AtomicInteger; public class Demo34 { // private static int count = 0; private static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; i < 50000; i++) { count.getAndIncrement(); // count++ // count.incrementAndGet(); // ++count // count.getAndDecrement(); // count-- // count.decrementAndGet(); // --count // count.getAndAdd(10); // count+= 10 } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 50000; i++) { // count++; count.getAndIncrement(); } }); t1.start(); t2.start(); t1.join(); t2.join(); // 通过 count.get() 拿到原子类内部持有的真实数据. System.out.println("count = " + count.get()); } }
-