AbstractQueuedSynchronizer是 java同步中基礎類 是用來實現同步的java
可是爲何咱們在平時的使用中 卻感受歷來沒有用到過這個類呢?緩存
這個是由於同步工具類中的內部類 繼承AQS 這個內部把AQS封裝起來 讓咱們察覺不到這個類的存在安全
AQS中使用模板方法來實現同步機制 在Android中的View的onDraw onLayoutmarkdown
若是咱們要實現獨佔 咱們須要實現 tryAcquire方法 併發
若是咱們要實現共享 咱們須要實現 tryAcquireShared方法ide
實現同步工具類關鍵是state這個變量 缺省都是0工具
那麼咱們要實現 顯示獨佔鎖 就繼承Lock性能
那麼lock和unlock 要如何實現鎖呢 這個時候就要借用AQS的ui
獨佔 不可重入的鎖spa
public class SelfLock implements Lock {
//內部類繼承AQS
private static class Sync extends AbstractQueuedSynchronizer {
/*得到鎖*/
@Override
protected boolean tryAcquire(int arg) {
//經過設置指望state是0 而且設置state爲1 爲true的時候就表明拿到了鎖
if (compareAndSetState(0, 1)) {
//告訴別人我拿到這個線程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
/*釋放鎖*/
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
}
private final Sync sync=new Sync();
@Override
public void lock() {
//得到鎖
sync.acquire(1);
}
@Override
public void unlock() {
sync.release(1);
}
複製代碼
這樣咱們就是實現了 同步鎖
一個代碼塊中 有一把鎖, 任意時刻只有一個線程能拿到這把鎖 其餘線程都在外面排隊 因此就是沒有拿到鎖的線程要在外面排隊。
因此把排隊的線程打包成QNode對象
QNode:
把排隊的線程 QNode造成鏈表
例如: 假如線程A已經排隊拿鎖了,那麼線程B要去拿鎖就會在 將本身的myPred指向 A的QNode節點(而且locked=true)
是這樣由於 擁有前一個節點的 因此B就不斷的自旋CAS 檢查locked的值 若是locked=false了,那麼就代碼前一個線程A釋放了鎖會把本身的locked設置爲false B線程就能夠拿到鎖了。
考慮到性能 AQS採用雙向鏈表 並不會不斷自旋 會嘗試自旋必定次數後,將線程掛起
wait和notify notifyall 也有 一個 等待隊列 跟這個拿鎖的隊列同樣
公平鎖就是 全部的線程都要走排隊的流程 CLH拿鎖的流程就是 公平鎖
公平鎖代碼以下:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//hasQueuedPredecessors 公平鎖會判斷是否有隊列排隊若是有就排隊
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
複製代碼
非公平鎖就是 已經有隊列了能夠不排隊直接插隊拿鎖就是非公平鎖
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//非公平鎖,直接去拿鎖
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
複製代碼
可重入鎖其實就是 state的狀態變化
private static class RetainSync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
} else if (getExclusiveOwnerThread() == Thread.currentThread()) {
setState(getState() + 1);
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (getExclusiveOwnerThread() == Thread.currentThread()) {
throw new IllegalMonitorStateException();
}
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setState(getState() - 1);
if (getState() == 0) {
setExclusiveOwnerThread(null);
}
return true;
}
}
複製代碼
由於Cpu 讀取內存 須要100納秒 cpu執行一條指令須要0.6納秒
若是執行a+b 讀取a 須要100納秒 讀取b也須要100納秒 +須要0.6納秒 總共執行時間爲200.6納秒
CPU就引入了一個高速緩存
L1 L2 L3
工做內存包含 CPU寄存器 CPU緩存(99%) 主內存(1%) 主內存很是少
主內存包含 CPU緩存(1%) 主內存(99%)(就是電腦內存條) cpu緩存很是少
工做內存是線程獨享的
好比某個線程要對 int count 變量進行累加 count 存儲在主內存中
就會在向這個線程的工做內存建立一個count的一個副本,線程不能操做主內存中的count, 只能操做工做線程中count副本
能夠看到 count=count+1; 因爲工做內存的存在 這個方法並非安全的。 線程中的工做內存 對於同一個變量 是由可見性的問題的。 那麼如何解決可見性問題
volatile 只能保證可見性 可是不能保證原子性。
volatile 能夠對簡單的賦值操做 可是對於複雜複合操做不能保證線程安全
volatile 能夠應用在 一個線程寫 多個線程讀取的場景中使用