1 了解AQS
要求:能够画出AQS自旋锁的图且复述出整个过程
AQS (AbstractQueuedSynchronizer)是一个帮助器,自定义锁的一个帮助器(体现在代码上 私有的内部类继承AQS)
2 了解AQS的API
要求:通过java.util.concurrent.locks 下的 Lock和 AbstractQueuedSynchronizer的api了解AQS
Acquire()
尝试获取锁,如果没有获取到锁的画,就把当前节点放置到队列的末尾
3 能够通过AQS写一个锁
自定义锁
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(); }}
使用自定义锁
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(); }}