Lock
1 | Lock lock = new ReentrantLock(); |
1、灵活
2、尝试非阻塞地获取锁1
2
3public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
3、能被中断地获取锁1
2
3
4// 中断获取的一部分,parkAndCheckInterrupt 调用的是LockSupport.park(this); LockSupport 具有中断返回的特性。
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
4、能超时获取锁1
lock.tryLock(1, TimeUnit.SECONDS);
5、独占或共享的方式
队列同步器
- 模板方法
- volatile int 状态变量
- CAS
- 同步队列(FIFO双向队列)
- 共享、独占获取同步状态
1 | // override |
1 | // 可用方法 |
1 | // 提供的模板方法 |
独占式
获取
1 | public final void acquire(long arg) { |
释放
1 | public final boolean release(int arg) { |
共享式
获取
1 | public final void acquireShared(int arg) { |
释放
1 | public final boolean releaseShared(int arg) { |
- 独占式
- 共享式
- 超时获取
- 同步队列
1 | abstract static class Sync extends AbstractQueuedSynchronizer { |
ReentrantLock
AQS的唤醒机制是从队列中取出head节点的下一个节点来唤醒。咋一看,好像公平锁和非公平锁的实现逻辑是一样的。
其实,非公平锁在唤醒下一个节点后,释放锁的节点或者新加入线程会和被唤醒的节点同时来竞争锁,并且释放锁的节点很大几率是成功的,所以不是FIFO,即非公平锁。
而公平锁,由于在获取的时候添加了判断hasQueuedPredecessors(),即使当前新线程并没有在队列中,也会生成一个新的节点插入队尾,并不会和唤醒节点竞争。因此是严格FIFO。
获取锁
非公平锁
1 | final void lock() { |
公平锁
1 | final void lock() { |
释放锁
1 | public final boolean release(int arg) { |
ReentrantReadWriteLock
通过按位切割使用,在一个整型变量上维护多种状态。高16位表示读。低16位表示写。
写锁的获取与释放
1 | protected final boolean tryAcquire(int acquires) { |
存在读锁时,不能获取写锁。
读锁的获取与释放
1 | protected final int tryAcquireShared(int unused) { |
写锁没有被其他线程获取或者被自己获取,则获取读锁成功;否则失败。
锁降级
拥有写锁,再获取读锁,随后释放写锁的过程。
LockSupport
1 | void park() |
LockSupport.part()方法是响应中断地,当线程中断后,会从park方法返回执行后续逻辑,所以,LockSupport中的对中断地响应可以灵活控制。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/**
* @author joyo
* @date 2018/4/16
*/
public class LockSupportInterruptTest {
public static void main(String[] args) throws InterruptedException {
ReentrantLock lock = new ReentrantLock();
Thread thread = new Thread(new Runnable() {
public void run() {
lock.lock();
try {
LockSupport.park();
System.out.println("come back here");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
}
最终输出结果:come back here,而不是打印异常栈。
而Object.wait()方法并没有这个特性,会直接抛出中断异常。
Condition
- condition包含一个等待队列,有收尾节点组成。
- await:生成节点放入等待队列
- signal:将节点从等待队列中放入到sync中的同步队列末尾,等待后续竞争锁,恢复执行。唤醒时,是从头结点开始一个一个唤醒的。
- ConditionObject是AQS的内部类,当唤醒Condition等待队列的节点时,会加入到AQS的同步队列的尾巴。
1 | public final void await() |
1 | // 队头、队尾 |