1. 前言 本节内容主要是对 J**a 并发锁之 Lock 接口进行介绍,Lock 是类似于 synchronized 的另外一种锁的使用,那么本节我们会对 Lock 进行详细的介绍,主要知识点如下: Lock 接口的介绍,这是我们开始认识 Lock 的敲门砖,本节课程的基础知识; Lock 接口相比于 synchroni...
本节内容主要是对 J**a 并发锁之 Lock 接口进行介绍,Lock 是类似于 synchronized 的另外一种锁的使用,那么本节我们会对 Lock 进行详细的介绍,主要知识点如下:
Lock 是一个接口,并非一个实现类,本节内容主要对 Lock 接口进行一个意义、结构及方法的介绍,为后续讲解 Lock 接口的实现类常用锁奠定一个扎实的基础。
Lock 接口的诞生:在 J**a 中锁的实现可以由 synchronized 关键字来完成,但在 J**a5 之后,出现了一种新的方式来实现,即 Lock 接口。
诞生的意义:Lock 接口支持那些语义不同(重入、公平等)的锁规则,可以在非阻塞式结构的上下文(包括 hand-over-hand 和锁重排算法)中使用这些规则。主要的实现是 ReentrantLock。对于 ReentrantLock,后续有专门的小节进行讲解。
JDK 1.5 前的 synchronized:在多线程的情况下,当一段代码被 synchronized 修饰之后,同一时刻只能被一个线程访问,其他线程都必须等到该线程释放锁之后才能有机会获取锁访问这段代码。
Lock 接口: 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
Lock 相对于 synchronized 关键字而言更加灵活,你可以自由得选择你想要加锁的地方。当然更高的自由度也带来更多的责任。
使用示例:我们通常会在 try catch 模块中使用 Lock 关键字,在 finally 模块中释放锁。
Lock lock = new ReentrantLock(); //通过子类进行创建,此处以ReentrantLock进行举例
lock.lock(); //加锁
try {
// 对上锁的逻辑进行操作
} finally {
lock.unlock(); //释放锁
}
我们通过两个个案例分析来了解 Lock 接口的优势所在。
案例 1 :在使用 synchronized 关键字的情形下,假如占有锁的线程由于要等待 IO 或者其他原因(比如调用 sleep 方法)被阻塞了,但是又没有释放锁,那么其他线程就只能一直等待,别无他法。这会极大影响程序执行效率。
案例 1 分析:该案例体现了 synchronized 的缺陷,当线程被占有时,其他线程会陷入无条件的长期等待。这是非常可怕的,因为系统资源有限,最终可能导致系统崩溃。
案例 1 解决:Lock 接口中的 tryLock (long time, TimeUnit unit) 方法或者响应中断 lockInterruptibly () 方法,能够解决这种长期等待的情况。
案例 2 :我们知道,当多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作也会发生冲突现象,但是读操作和读操作不会发生冲突现象。
但是如果采用 synchronized 关键字实现同步的话,就会导致一个问题,即当多个线程都只是进行读操作时,也只有一个线程可以进行读操作,其他线程只能等待锁的释放而无法进行读操作。
案例 2 分析:该案例体现了 synchronized 的缺陷,悲观锁的缺陷。我们说过,如果只是读操作,没有增删改操作的话,多线程环境下无需加锁。但是这种情况下,如果在同一时间多个线程进行读操作,synchronized 会 block 其他的读操作,这是不合理的。
案例 2 解决:Lock 接口家族也可以解决这种情况,后续我们会对 ReadWriteLock 接口的一个子类 ReentrantReadWriteLock 进行讲解。
总结:Lock 接口实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作,能够解决 synchronized 不能够避免的问题。
我们来简单的看下,JDK 中 Lock 接口的源码中所包含的方法:
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
方法介绍:
Tips:对 Lock 接口方法的使用,我们必须基于子类进行 Lock 的创建来展示,由于目前我们还未接触 Lock 接口的实现子类,此处只做方法的介绍。后续对 ReentrantLock 进行讲解时,会进行深入讲解。
本节主要是对 Lock 接口的常用方法进行了介绍,为本节内容的核心知识。除了方法的介绍外,本节内容不容忽视的一个重点内容是 synchronized 关键字与 Lock 接口的区别,以及 Lock 接口的优势所在。
掌握本节内容,有助于同学对后续实现类锁的学习,为后续的学习奠定了良好的基础。