Lock & Condition

2020/12/31 posted in  并发工具

Java 通过Lock和Condition 两个接口实现管程,Lock解决互斥问题,Condition解决同步问题

再造管程

解决死锁时,针对不可抢占条件,synchronized没有办法解决

解决不可抢占方案

  1. 能够响应中断:给阻塞的线程发送中断信号后,阻塞线程能够被唤醒
  2. 支持超时:如果一个线程在一段时间之内没有获取锁,不是进入阻塞状态,而是返回一个错误
  3. 非阻塞获取锁:如果一个线程尝试获取锁失败,并不进入阻塞状态,而是直接返回
// 支持中断的API
void lockInterruptibly() 
  throws InterruptedException;
// 支持超时的API
boolean tryLock(long time, TimeUnit unit) 
  throws InterruptedException;
// 支持非阻塞获取锁的API
boolean tryLock(); 

锁实现的原理

Java中ReentrantLock,内部持有volatile变量state,获取锁和解锁都会读取state的值

  1. 顺序性规则:对于线程T1, value+=1 Happens-Before释放锁的unlock
  2. volatile规则:由于state = 1 会先读取state,所以T1的unlock 操作Happens-Before T2的lock操作
  3. 传递性规则:线程T1的 value += 1 Happens-Before T2的lock

可重入锁、公平锁和非公平锁

  1. 可重入锁:同一线程可以重复获取同个锁
  2. 公平锁和非公平锁:提供保证谁等待的时间长唤醒谁的就是公平的

用锁最佳实践

  1. 永远只在更新对象的成员变量时加锁
  2. 永远只在访问可变的成员变量时加锁
  3. 永远不在调用其他对象的方法时加锁


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();
    }  
  }
}

异步方法和异步调用

  1. 异步调用:调用发创建一个子线程,在子线程中执行方法调用
  2. 异步方法:方法实现时,创建一个新线程执行主要逻辑,主线程返回