1.前言
通过前面的AQS的基本原理了解:之后:Java 并发 - AQS:框架分析,我了解了大概的AQS的一整个流程,也明白了大部分的同步工具都是基于AQS来实现的,好像比较重要的就是重写tryAcquire 和 tryRelease 两个方法而已,那么我借鉴了其他同步工具的写法,试了试尝试自己实现一个基于AQS的同步工具,看看能不能正常跑起来。
以下是基于独占式的写法,并不是共享式的,所以实现的也是tryAcquire
和tryRelease
。主要想做的事情就是同一个时刻只能让一个线程一直抱有资源做一件事情,直到这件事情做完了之后,才可以让其他线程去做一些事情,这不就是同步的概念嘛!
2.自定义工具
实现自定义同步器需要实现tryAcquire
和tryRelease
,这里再重新提及一下state状态的意思,他代表当前锁的状态,0代表没有被占用,大于 0 代表有线程持有当前锁。
具体Demo如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public class LockBaseAQS {
private static class Sync extends AbstractQueuedSynchronizer { @Override protected boolean tryAcquire (int arg){ return compareAndSetState(0, 1); } @Override protected boolean tryRelease (int arg){ setState(0); return true; }
@Override protected boolean isHeldExclusively() { return getState() == 1; } }
private Sync sync = new Sync();
public void lock() { sync.acquire(1); }
public void unLock() { sync.release(1); } }
|
这里我想还是把acquire和release的源码放出来,比较容易理解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
|
3.测试
编写了一个测试Demo如下,我们创建两个线程,然后启动这两个线程,让两个线程各自从0打印10000000,看结果会不会统一。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package MyLock;
public class Test {
static int count = 0; static LockBaseAQS myLock = new LockBaseAQS();
public static void main(String[] args) throws InterruptedException { Runnable runnable = new Runnable() { @Override public void run () { try { myLock.lock(); for (int i = 0; i < 10000000; i++) { count++; } } catch (Exception e) { e.printStackTrace(); } finally { myLock.unLock(); }
} }; Thread thread1 = new Thread(runnable); Thread thread2 = new Thread(runnable); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(count); } }
|
这样一来每次都会打印出:
假如将同步方法给屏蔽了之后,会发现每次都打印出不一样的数字!