要求:可以畫出AQS自旋鎖的圖且複述出整個過程java
AQS (AbstractQueuedSynchronizer)是一個幫助器,自定義鎖的一個幫助器(體如今代碼上 私有的內部類繼承AQS)api
要求:經過java.util.concurrent.locks 下的 Lock和 AbstractQueuedSynchronizer的api瞭解AQS安全
Acquire()ide
嘗試獲取鎖,若是沒有獲取到鎖的畫,就把當前節點放置到隊列的末尾測試
自定義鎖ui
package src.main.concurrent.AQS; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.AbstractQueuedSynchronizer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; /** * 自定義鎖 * 1 實現Lock * 2 AQS 私有內部類繼承AQS * 3 重寫tryAcquire/tryRelease * compareAndSetState 經過CAS方式修改值 * setExclusiveOwnerThread 設置當前值佔有資源 * * 可重入鎖,在MyLock基礎之上修改 獲取鎖 釋放鎖部分邏輯 * * @author liuhuxiang * @version $Id: MyLock.java, v 0.1 2018年10月09日 21:18 liuhuxiang Exp $ */ public class MyLock implements Lock { private AQSHelper aQSHelper = new AQSHelper(); // 這裏就能夠體現AQS是一個幫助類 private class AQSHelper extends AbstractQueuedSynchronizer { //獲取鎖 //注意這裏的arg表示信號量,正常來講arg爲1 @Override protected boolean tryAcquire(int arg) { //當前資源沒有線程佔用 if (getState() == 0) { //經過CAS方式獲取原子性的修改 if (compareAndSetState(0, arg)) { //設置當前值佔有資源 setExclusiveOwnerThread(Thread.currentThread()); return true; } //可重入鎖--獲取鎖修改 只要在AQS基礎之上加上一個判斷便可 }else if(getExclusiveOwnerThread()==Thread.currentThread()){ setState(getState()+arg); return true; } return false; } //釋放鎖 @Override protected boolean tryRelease(int arg) { //減去信號量 int state = getState() - arg; boolean flag = false; //判斷釋放後是否爲0 if (state == 0) { setExclusiveOwnerThread(null); setState(state); return true; } //存在線程安全嗎?重入性的問題,當前已經獨佔了資源()state,因此這裏不存在安全問題 //可重入鎖--釋放鎖修改 這裏不要作任何修改,等於獲取鎖加了兩邊arg,釋放鎖減掉兩邊arg setState(state); return false; } //這裏參考jdk public Condition newConditionObject() { return new ConditionObject(); } } @Override public void lock() { aQSHelper.acquire(1); } @Override public void lockInterruptibly() throws InterruptedException { aQSHelper.acquireInterruptibly(1); } @Override public boolean tryLock() { return aQSHelper.tryAcquire(1); } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { //試圖以獨佔模式獲取對象,若是被中斷則停止,若是到了給定超時時間,則會失敗。 return aQSHelper.tryAcquireNanos(1, unit.toNanos(time)); } @Override public void unlock() { aQSHelper.release(1); } @Override public Condition newCondition() { return aQSHelper.newConditionObject(); } }
使用自定義鎖spa
package src.main.concurrent.AQS; /** * 測試MyLock * 建立20個線程,每一個線程調用++方法,驗證加鎖不加鎖兩種方式 * * @author liuhuxiang * @version $Id: TestMyLock.java, v 0.1 2018年10月09日 21:37 liuhuxiang Exp $ */ public class TestMyLock { private int m = 0; MyLock myLock = new MyLock(); private int increase() { myLock.lock(); try { return m++; } finally { //確保最終釋放鎖 myLock.unlock(); } } public static void main(String[] args) { TestMyLock testMyLock = new TestMyLock(); Thread[] threads = new Thread[20]; for (int i = 0; i < 20; i++) { threads[i] = new Thread(() -> { System.out.println(testMyLock.increase()); }); threads[i].start(); } } }
探討可重入鎖線程
package src.main.concurrent.AQS; /** * 探討重入性問題 * 調用的時候,發現只打印了a ,爲何只打印了a,由於a()方法佔用了鎖,資源不爲0了,因此b沒法暫用資源 * * 因此要去修改MyLock,多加一個可重入性的判斷 * * 可重入性:同一個鎖多同一資源進行佔有的時候,直接分配給這個線程 * * @author liuhuxiang * @version $Id: TestMyLock2.java, v 0.1 2018年10月09日 21:46 liuhuxiang Exp $ */ public class TestMyLock2 { private int m = 0; MyLock myLock = new MyLock(); public void a() { myLock.lock(); System.out.println("a"); b(); myLock.unlock(); } public void b() { myLock.lock(); System.out.println("b"); myLock.unlock(); } public static void main(String[] args) { TestMyLock2 testMyLock2 = new TestMyLock2(); new Thread(() -> { testMyLock2.a(); }).start(); } }