5. Java中的锁
2020-02-25 本文已影响0人
ygxing
本文将介绍Java并发包中与锁相关的API和组件, 以及这些API和组件的使用方式和实现细节
1. Lock接口
锁是用来控制多个线程访问共享资源的方式, 像独占锁, 读写锁
在Lock接口出现之前, java程序考synchronized关键字实现锁功能, 在JDK1.5之后, 并发包中新增了Lock接口, 用来实现锁功能
Lock使用方式, 有两个注意点:
- 在finally中释放锁, 目的是保证在获取到锁之后, 最终能够被释放
- 在try语句之前获取锁, 因为获取锁的过程中可能会产生异常, 而获取不到锁, 获取不到锁, 再去释放锁, 就会抛出IllegalMonitorStateException异常
public class LockExample {
public static void main(String[] args) {
// 实例化锁
Lock lock = new ReentrantLock();
// 获取锁
lock.lock();
try {
// doSomething...
} finally {
// 释放锁
lock.unlock();
}
}
}
1.1 Lock接口和synchronized关键字对比, 优势
- 尝试非阻塞的获取锁, 当线程尝试获取锁, 如果这一时刻锁没有被其他线程获取到, 则成功获取并持有锁
- 能够被中断的获取锁, 获取到锁的线程能够响应中断, 当获取到锁的线程被中断时, 中断异常将会被抛出, 同时锁会被释放
- 超时获取锁, 在指定的截止时间内获取锁, 如果超时之后仍旧无法获取锁, 那么立即返回
1.2 Lock接口API
- void lock(), 获取锁, 调用该方法时, 当前线程将会获取锁, 当锁获取之后, 从该方法返回
- void lockInterruptibly(), 可中断的获取锁, 和lock()方法的不同之处在于该方法会相应中断, 即在锁的获取过程中, 可以终端当前线程
- boolean tryLock(), 尝试非阻塞的获取锁, 调用该方法后立即返回
- 如果能获取到锁, 返回true
- 如果获取不到锁, 返回false
- void unlock(), 释放锁
- Condition newCondition(), 获取等待通知组件, 该组件和当前的锁绑定, 当前线程获取锁之后, 才能调用Condition组件的方法, Condition接口与Lock接口配合, 实现了线程等待/通知机制, 实现了Object类的wait和notify的功能
- Condition#await()作用: 当前线程将释放锁, 当其他线程调用Condition#signal或signalAll方法, 当前线程获取到锁之后, 才会从await()方法返回, 作用类似于Object#wait(), 如果当前线程等待过程中被中断, 那么抛出被中断异常
- Condition#signal()作用, 唤醒一个等待Condition的线程, 将该线程从等待队列移动到同步队列中, 被唤醒队列参与到Lock的竞争中, 如果获取到Lock, 会从等待方法中返回
2. 队列同步器AQS
队列同步器AbstractQueuedSynchronizer,用来构建锁或者其他同步其他组件的基础框架,它使用int类型的成员变量state表示同步状态,通过内置的FIFO队列来完成线程排队工作
同步器是实现锁的关键,利用同步器实现锁的语义
- 锁面向使用者, 定义了使用者与锁交互的接口, 隐藏了锁的实现细节
- 同步器面试锁的实现者, 简化了锁的实现方式, 屏蔽了同步状态管理, 线程排队, 等待与唤醒等底层操作
- 锁和同步器很好的隔离了使用者和实现者所需关注的领域
2.1 同步器DEMO
独占锁MutexLock是一个自定义同步组件, 再同一时刻只有一个线程站有锁
因为是独占锁, 所以Sync不需要实现acquireShared和releaseShared等方法
//独占锁
public class MutexLock implements Lock {
//同步器
private Sync sync = new Sync();
//独占上锁, 不响应中断
//先尝试获取锁
//失败的话新建一个Node节点, 将当前线程加入到同步队列尾部
//再尝试CAS获取锁
//获取不到锁的话, 进入等待状态, 等待被唤醒
//唤醒之后继续获取锁
@Override
public void lock() {
sync.acquire(1);
}
//响应中断的上锁
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//尝试获取锁
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
//在时间内尝试获取锁
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
//释放锁
//先尝试释放锁
//释放锁之后
//唤醒等待中的线程
@Override
public void unlock() {
sync.release(1);
}
//Condition类, 多线程间协调通信的工具类
//Condition#await()方法相当于Object#wait()方法
//Confition#signal()方法相当于Object#notify()方法
@Override
public Condition newCondition() {
return sync.newCondition();
}
//同步器
private class Sync extends AbstractQueuedSynchronizer {
//尝试获取锁
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
//CAS将state字段设置为1
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//尝试释放锁
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
//将state设为0
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
//独占锁
return true;
}
Condition newCondition() {
return new ConditionObject();
}
}
}
2.2 同步器实现原理
同步器主要由同步队列, 独占状态的获取与释放, 共享状态的获取与释放等模板方法构成