Java 通过Lock和Condition 两个接口实现管程,Lock解决互斥问题,Condition解决同步问题
再造管程
解决死锁时,针对不可抢占条件,synchronized没有办法解决
解决不可抢占方案
- 能够响应中断:给阻塞的线程发送中断信号后,阻塞线程能够被唤醒
- 支持超时:如果一个线程在一段时间之内没有获取锁,不是进入阻塞状态,而是返回一个错误
- 非阻塞获取锁:如果一个线程尝试获取锁失败,并不进入阻塞状态,而是直接返回
// 支持中断的API
void lockInterruptibly()
throws InterruptedException;
// 支持超时的API
boolean tryLock(long time, TimeUnit unit)
throws InterruptedException;
// 支持非阻塞获取锁的API
boolean tryLock();
锁实现的原理
Java中ReentrantLock,内部持有volatile变量state,获取锁和解锁都会读取state的值
- 顺序性规则:对于线程T1, value+=1 Happens-Before释放锁的unlock
- volatile规则:由于state = 1 会先读取state,所以T1的unlock 操作Happens-Before T2的lock操作
- 传递性规则:线程T1的 value += 1 Happens-Before T2的lock
可重入锁、公平锁和非公平锁
- 可重入锁:同一线程可以重复获取同个锁
- 公平锁和非公平锁:提供保证谁等待的时间长唤醒谁的就是公平的
用锁最佳实践
- 永远只在更新对象的成员变量时加锁
- 永远只在访问可变的成员变量时加锁
- 永远不在调用其他对象的方法时加锁
Condition
实现阻塞队列
public class BlockedQueue<T>{
final Lock lock =
new ReentrantLock();
// 条件变量:队列不满
final Condition notFull =
lock.newCondition();
// 条件变量:队列不空
final Condition notEmpty =
lock.newCondition();
// 入队
void enq(T x) {
lock.lock();
try {
while (队列已满){
// 等待队列不满
notFull.await();
}
// 省略入队操作...
//入队后,通知可出队
notEmpty.signal();
}finally {
lock.unlock();
}
}
// 出队
void deq(){
lock.lock();
try {
while (队列已空){
// 等待队列不空
notEmpty.await();
}
// 省略出队操作...
//出队后,通知可入队
notFull.signal();
}finally {
lock.unlock();
}
}
}
异步方法和异步调用
- 异步调用:调用发创建一个子线程,在子线程中执行方法调用
- 异步方法:方法实现时,创建一个新线程执行主要逻辑,主线程返回
