在多线程编程中,锁是必不可少的,因为许多并发问题都可以通过加锁来解决,例如线程安全的单例实现、线程安全的数据结构实现等等。Java提供了许多类型的锁,本文就来介绍其中三种常见的锁:ReentrantLock、ReentrantReadWriteLock、StampedLock。
ReentrantLock(重入锁)是Java中的一个独占锁,也是一种可重入锁。独占锁是指同一时刻只能有一个线程获得锁,其他线程必须等待;可重入锁是指线程可多次获取同一把锁,不会自己阻塞自己。
使用ReentrantLock的基本方法如下:
ReentrantLock lock = new ReentrantLock(); // 创建锁对象
lock.lock(); // 获取锁
try {
// 执行需要加锁的代码
} finally {
lock.unlock(); // 释放锁
}
在上面的代码中,首先创建一个ReentrantLock对象,然后在需要加锁的代码执行前调用lock()方法获取锁,在需要释放锁的地方使用unlock()方法释放锁。
需要注意的是,在使用ReentrantLock时,要保证锁的获取和释放要在try…finally块中进行。因为在试图获取锁的过程中,如果线程被中断(interrupt),那么当前线程也需要释放锁资源。
ReentrantLock有一些特性:
ReentrantLock是一种可重入锁,提供了公平锁和非公平锁、可中断获取锁和条件变量等特性,使用起来比较灵活,但也比较复杂,需要手动控制加锁和释放锁的过程。
ReentrantReadWriteLock(可重入读写锁)是Java中另一种常用的锁,也是一种可重入锁。与ReentrantLock不同的是,ReentrantReadWriteLock既可以支持独占锁,也可以支持共享锁。共享锁是指同一时刻可以有多个线程读取共享资源,但只能有一个线程写入共享资源;独占锁是指同一时刻只能有一个线程获得锁,其他线程必须等待。因此,ReentrantReadWriteLock适用于读多写少的场景,可以提高并发读操作的效率。
使用ReentrantReadWriteLock的基本方法如下:
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); // 创建锁对象
lock.readLock().lock(); // 获取读锁
try {
// 执行需要读取共享资源的代码
} finally {
lock.readLock().unlock(); // 释放读锁
}
在上面的代码中,首先创建一个ReentrantReadWriteLock对象,然后在需要读取共享资源的代码执行前调用readLock().lock()方法获取读锁,在需要释放锁的地方使用readLock().unlock()方法释放读锁。
如果需要写入共享资源,则需要获取写锁,相应地,释放锁时需要调用writeLock().unlock()方法。
需要注意的是,在使用ReentrantReadWriteLock时,要保证读锁和写锁的获取和释放要在try…finally块中进行。
ReentrantReadWriteLock有一些特性:
ReentrantReadWriteLock是一种可重入读写锁,适用于读多写少的场景,提供了公平锁和非公平锁、支持读写分离、降低锁的粒度等特性。使用起来比ReentrantLock稍微复杂一些,但也比较灵活。
StampedLock(标记锁)是Java 8中新增的锁机制。它是一种乐观锁,允许多个线程在没有互相干扰的情况下访问共享资源。相比于ReentrantLock和ReentrantReadWriteLock,StampedLock更适用于读多写少、并发度高的场景,也可以用于提高读操作的性能。
StampedLock的基本使用方法如下:
StampedLock lock = new StampedLock(); // 创建锁对象
long stamp = lock.tryOptimisticRead(); // 尝试获取乐观读锁
// 执行读操作
if (lock.validate(stamp)) {
// 检查是否可用
// 执行读操作
} else {
// 尝试获取悲观读锁
stamp = lock.readLock(); // 获取悲观读锁
try {
// 执行读操作
} finally {
lock.unlockRead(stamp); // 释放悲观读锁
}
}
在上面的代码中,首先创建一个StampedLock对象,然后使用tryOptimisticRead()方法尝试获取乐观读锁。如果获取成功,则可以执行读操作;如果获取失败,则需要使用读锁(悲观锁)来获取资源。在获取锁后,执行完读操作后需要释放锁。
StampedLock还提供了其他类型的锁,如悲观读锁、写锁等。使用方法和ReentrantReadWriteLock类似。