要求:可以画出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(); } }