并发-lock锁

30 阅读2分钟

虽然java通过synchronized可以解决线程并发的同步的问题,同时也对synchronized进行了优化,提升了性能,但是它获取锁的操作是隐式获取的,所有很多时候我们没办法更加细粒度的控制,于是就有了lock对象,帮助我们能显示的控制锁。

原理

我们一般使用lock是这样的。

1 Lock lock = new ReentrantLock(); //这里可以是自己实现Lock接口的实现类,也可以是jdk提供的同步组件
2 lock.lock();//一般不将锁的获取放在try语句块中,因为如果发生异常,在抛出异常的同时,也会导致锁的无故释放
3 try {
4 }finally {
5     lock.unlock(); //放在finally代码块中,保证锁一定会被释放
6 }

打开Lock接口,我们会发现,它基本上把加锁的整个流程进行了抽象。我们只需要按照对应的流程去完成加锁逻辑即可。

public interface Lock {

    // 获取锁
    void lock();

    // 响应中断
    void lockInterruptibly() throws InterruptedException;

    // 尝试获取锁
    boolean tryLock();

    // 尝试获取锁,设置超时时间
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    // 获取等待通知组件,该组件和当前锁绑定
    Condition newCondition();

然后你就会发现一个尴尬的事,我们抽象了整个加锁的流程,但是怎么拿到锁呢?总不可能还是通过synchronized去获取锁把。这里就要稍微引入另一个概念,那就是aqs,简单来说他就是一个同步器,通过一个对状态的控制保证线程并发的安全性。

也就是说Lock规范了加锁的流程,保证开发者使用每个lock的逻辑是相同的,aqs控制获取锁的逻辑,保证我们获取锁的策略的灵活性。

总结

简单理解Lock是将加锁的流程进行抽象,保证开发者加锁的流程统一,但同时将获取锁的流程通过aqs进行抽离,保证获取锁和加锁之间的独立。这样既能保证使用者,在使用不同锁的时候,流程是一致的。但是我们还可以通过不同的aqs进行组合,提供不同的加锁策略。