[相关源码]
(https://github.com/Wasabi1234/Java-Concurrency-Progamming-Tutorial)
1 Unsafe 类的 park 和 unpark
- public native void park(boolean var1, long var2);
- public native void unpark(Object var1);
park 方法用来阻塞一个线程, 第一个参数用来指示后面的参数是绝对时间还是相对时间, true 表示绝对时间, false 表示从此刻开始后的相对时间. 调用 park 的线程就阻塞在此处.
unpark 用来释放某个线程的阻塞, 线程用参数 var1 表示
举个例子:
2 LockSupport
直接使用 Unsafe 还是有诸多不便之处, 因此 lock 包提供了一个辅助类 LockSupport 封装了 park 和 unpark
举个例子:
可以看出, 使用 LockSupport 要比直接只用 Unsafe 更加便捷.
此外, LockSupport 还可以用来给线程设置一个 Blocker 对象, 便于调试和检测线程, 其原理是使用 Unsafe 的 putObject 方法直接设置 Thread 对象的 parkBlocker 属性, 并在合适的时候读取这个 Blocker 对象, 例子如下:
3 AQS 同步器
各种锁 ReentrantLock,ReentrantReadWriteLock 以及各种同步器诸 Semaphore,CountDownLatch 等, 核心都是 AbstractQueuedSynchronizer
要真正从源头理解 AQS, 建议仔细阅读该类的设计者的论文
(http://gee.cs.oswego.edu/dl/papers/aqs.pdf)
3.1 使用 AQS 手写排它锁
让我们先具体感知它是如何使用的.
这里有一个非常简单的例子 SimpleLock, 实现了一个最简单的排它锁.
当有线程获得锁时, 其他线程只能等待
当这个线程释放锁时, 其他线程可以竞争获取锁
运行结果表明, 通过简单的几行代码, 就实行了一个锁的所有功能.
根据 JUC 作者的建议, AQS 的使用方法要遵循上面这个模式.
使用一个内部类 Sync 来继承 AQS, 并实现 AQS 的相关方法
一般是
- tryAcquire
- tryRelease(排它锁)
或者
- tryAcquireShared
- tryReleaseShared(共享锁)
在内部使用代理模式实现锁的功能
这样可以让暴露出的同步, 互斥方法名由程序员自行决定.
例如各种锁可以使用
lock
unlock
Semaphore 可以使用
acquire
release
CountDownLatch 可以使用
await
countDown
2 AQS 基本原理
要实现一个同步器, 需要三个条件:
一个同步状态值, 且可原子操作该状态值. 显然 CAS 胜任
阻塞线程和解除阻塞线程的机制, 可以用 LockSupport 来实现
维护一个阻塞线程的队列, 并在队列的每个节点中存储线程的状态等信息
让我们看看 AQS 又是如何设计满足的这三个条件.
2.1 状态值及相应的操作方法
- private volatile int state;
- protected final int getState() {
- return state;
- }
- protected final void setState(int newState) {
- state = newState;
- }
- protected final boolean compareAndSetState(int expect, int update) {
- // See below for intrinsics setup to support this
- return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
- }
state 为 volatile int 型, 它的 CAS 方法, 提供原子的比较更新操作.
一般, AQS 认为
state == 0 时, 同步器处于释放状态, 多线程此时可竞争获取同步器
state ≠ 0 时, 同步器处于已获取状态, 后续线程需进入队列, 等待同步器 (可重入同步器允许获取同步器的线程再次进入该同步器, 此时使用 state 计数)
当然, 很多情况下, 程序员也可自己定义 state 的值的含义, 特别是在实现读写锁时, 需要将 state 一分为二的用.
2.2 阻塞和解除阻塞
LockSupport 提供了阻塞和解除阻塞的功能. 因此, 所有同步器的阻塞操作其实都是基于 LockSupport 的, 也就是基于 Unsafe 的 park 和 unpark 方法的.
2.3 线程等待队列
AQS 内部提供了一个 Node 类型, 它是用来形成 "线程等待队列" 的节点类型, 以及一个由 Node 类型组成的队列.
来源: http://www.jianshu.com/p/d01d5f143a36