1. 两个通用类
ReentrantLock类和ReentrantReadWriteLock类。
2. 作用与区别
- 都可以实现类似synchronized关键字的功能,通过lock()方法和unlock()方法实现锁的获取与释放。
- 不同点在于ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后面的任务,不管执行的内容是否都需要同步执行。而ReentrantReadWriteLock有两个锁,一个是读操作相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫排他锁。
- 读写锁的规则互斥规则为读锁之间不互斥,读锁与写锁互斥(先读后写或先写后读均互斥),写锁与写锁互斥。
3. 公平锁与非公平锁
锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁。
公平锁/非公平锁可以通过构造器ReentrantLock(boolean isFair)来创建,默认情况下,ReentrantLock类使用的是非公平锁。
4. Lock常用方法
- getHoldCount()
查询当前线程保持此锁定的个数,也就是调用lock方法的次数。 - getQueueLength()
返回正等待获取此锁定的线程估计数。 - getWaitQueueLength(Condition condition)
返回等待与此锁定相关的给定条件Condition的线程估计数。 - hasQueuedThread(Thread thread)
查询指定的线程是否正在等待获取此锁定。 - hasQueuedThreads()
查询是否有线程正在等待获取此锁定。 - hasWaiters(Condition condition)
查询是否有线程正在等待与此锁定有关的condition条件。 - isFair()
判断是不是公平锁 - isHeldByCurrentThread()
查询当前线程是否保持此锁定。 - isLocked()
查询此锁定是否由任意线程保持。 - lockInterruptibly()
如果当前线程未被中断,则获取锁定,如果已经被中断则出现异常。(如果用lock(),即使线程被中断,也不会出现异常,正常执行)。 - tryLock()
尝试获取锁,若获取成功,标记下是该线程获取到了锁,然后返回true;若获取失败,此时直接返回false。 - tryLock(long timeout, TimeUnit unit)
如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。 - lock()、lockInterruptibly()、tryLock()之间的关系
- 使用lock()获取锁,若获取成功,标记下是该线程获取到了锁(用于锁重入),然后返回。若获取失败,这时跑一个for循环,循环中先将线程阻塞放入等待队列,当被调用signal()时线程被唤醒,这时进行锁竞争(因为默认使用的是非公平锁),如果此时获取到了锁那么就返回,如果没获取到那么再次放入等待队列,等待唤醒,如此循环。其间就算外部调用了interrupt(),循环也会继续走下去。一直到当前线程获取到了这个锁,此时才处理interrupt标志,若有,则执行 Thread.currentThread().interrupt(),结果如何取决于外层的处理。
- 和lock()相比,lockInterruptibly()只有略微的修改,for循环过程中,如果检测到interrupt标志为true,则立刻抛出InterruptedException异常,这时程序便通过异常直接返回到最外层了,由外层继续处理,因此使用lockInterruptibly()时必须捕捉异常。
- 使用tryLock()尝试获取锁,若获取成功,标记下是该线程获取到了锁,然后返回true;若获取失败,此时直接返回false,告诉外层没有获取到锁,之后的操作取决于外层。
5. ReentrantLock代码示例
synchronized方法实现
public class MyService {
public void testMethod() {
synchronized (this) {
// 功能代码
}
}
}
Lock方法实现
public class MyService {
private Lock lock = new ReentrantLock();
public void testMethod() {
lock.lock();
// 功能代码
lock.unlock();
}
}
以上两种实现方式的执行效果是一样的。
6. ReentrantReadWriteLock代码示例
读锁
public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
try {
lock.readLock().lock();
// 业务代码
} finally {
lock.readLock.unlock();
}
}
}
写锁
public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void write() {
try {
lock.writeLock().lock();
// 业务代码
} finally {
lock.writeLock.unlock();
}
}
}
7. 类Condition
- 作用:通过await()方法和signal()方法可以实现等待/通知的功能,类似于Object的wait()和notify()方法,不过使用Condition更灵活,如“选择性通知”。
- Object类中的wait()方法相当于Condition类中的await()方法。
- Object类中的wait(long timeout)方法相当于Condition类中的await(long time, TimeUnit unit)方法。
- Object类中的notify()方法相当于Condition类中的signal()方法。
- Object类中的notifyAll()方法相当于Condition类中的signalAll()方法。
- 创建方式:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
- 注意事项:在condition.await()和condition.signal()方法调用之前需要调用lock.lock()代码获得同步监视器。
8. Condition常用方法
- awaitUninterruptibly()
类似await(),不过在等待过程中被中断时不抛中断异常。 - awaitUntil(Date deadLine)
类似await(),不过在等待时间到达后,如果可获取到锁,则自动唤醒。在等待时间到达前,也可以被其他线程唤醒。