Lock重入锁

前言

在并发编程中有两个核心问题, 一个是互斥另一个是同步。在java sdk并发包中,lock就是解决互斥问题,conditon就是解决同步问题。

Lock

lock有三个重要的特性:

  1. 能够响应中断: 线程在没有获取锁的时候,进入阻塞状态。我们能够给进入阻塞状态的线程发送信号,重新唤醒它。
  2. 支持超时: 在无法获取锁的时候,就等待一定的时间。超时就自动返回。
  3. 非阻塞的获取锁:如果无法获取锁就马上返回。
    以下为它的API
// 支持中断的 API
void lockInterruptibly() 
  throws InterruptedException;
// 支持超时的 API
boolean tryLock(long time, TimeUnit unit) 
  throws InterruptedException;
// 支持非阻塞获取锁的 API
boolean tryLock();

lock–内存可见性问题

可以先看代码

class X {
  private final Lock rtl =
  new ReentrantLock();
  int value;
  public void addOne() {
    // 获取锁
    rtl.lock();  
    try {
      value+=1;
    } finally {
      // 保证锁能释放
      rtl.unlock();
    }
  }
}

这段代码,如果T1线程将value改为1 那么t2线程在执行的时候能够看见吗?
答案是肯定的。
它是利用了volatile相关的happes-before规则。java sdk里面的ReentrantLock,内部持有一个volatile的成员变量state,获取锁的时候,会读写 state的值;解锁的时候,也会读写state,在执行value+=1之后,又读写了一次volatile变量state。根据相关的Happens-before规则:

  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()操作。
class SampleLock {
  volatile int state;
  // 加锁
  lock() {
    // 省略代码无数
    state = 1;
  }
  // 解锁
  unlock() {
    // 省略代码无数
    state = 0;
  }
}

lock—重入锁

ReentrantLock,指的是线程可以重复获取同一把锁。例如下面的代码,当线程T1执行到1处时,已经获取到了锁rtl,当在1处调用get()方法时,会在2处再次对锁rtl执行加锁操作。如果锁rtl是可重入的,那么线程T1可以再次加锁成功。如果锁rtl是不可重入的,那么线程T1此时会被阻塞。

class X {
  private final Lock rtl =
  new ReentrantLock();
  int value;
  public int get() {
    // 获取锁
    rtl.lock();         ②
    try {
      return value;
    } finally {
      // 保证锁能释放
      rtl.unlock();
    }
  }
  public void addOne() {
    // 获取锁
    rtl.lock();  
    try {
      value = 1 + get(); ①
    } finally {
      // 保证锁能释放
      rtl.unlock();
    }
  }
}

LOCK公平锁与非公平锁

ReentrantLock有两个构造方法。一个是无参构造,一个是传入fair。fair参数代表的是锁的公平策略,如果传入 true就表示需要构造一个公平锁,反之则表示构造一个非公平锁。

// 无参构造函数:默认非公平锁
public ReentrantLock() {
    sync = new NonfairSync();
}
// 根据公平策略参数创建锁
public ReentrantLock(boolean fair){
    sync = fair ? new FairSync() 
                : new NonfairSync();
}

公平锁就是先等待先唤醒,非公平锁是随机唤醒。

锁的使用经验

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

这三条中最后一条主要是担心在调用慢方法,如长时间IO或者业务复杂。 也会担心其他方法里面本身有锁,在调用的时候加锁会出现双重锁, 容易导致死锁。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值