基本介绍
- ReentrantLock是可重入锁,支持当前线程重入;
- ReentrantLock可以等待中断、可以实现公平锁、可以绑定多个条件;
- ReentrantLock和synchronized一样属于互斥锁,synchronized是非公平锁,默认情况下ReentrantLock也是不公平的,但可以通过构造函数入参设置为公平锁;
- ReentrantLock通过lock()方法获得锁,通过unlock()方法释放锁;
- ReentrantLock除了lock()方法获得锁,还可以使用tryLock()方法设置超时时间、使用lockInterruptibly()在等待锁期间响应中断;而synchronized则是一直等待无法响应中断;
- ReentrantLock可以使用newCondition()方法创建condition对象,来绑定子条件实现多条件关联;
ReentrantLock的缺点
- 在JDK1.5及之前,ReentrantLock能提供比synchronized更好的竞争性能;在JDK1.6及之后,synchronized使用的算法与ReentrantLock中的算法相似,两者性能差距不大;
- 需要由使用者来自行释放锁,且最好是在finally代码块中释放,使用者可能会忘记,所以无法保证安全性;
- synchronized还有一些优化措施,如线程封闭对象锁消除、增加锁粒度等优化措施等,可能会比使用ReentrantLock显示锁更优;
注意:ReentrantLock无法完全替代synchronized,只有在synchronized无法满足使用要求时(如获取锁超时、获取锁时响应中断等)才应该使用ReentrantLock。
ReentrantLock的使用
1. 使用ReentrantLock作为简单互斥锁
使用ReentrantLock作为竞态条件,获得锁则将count累加一次。
public class ReentrantLockTest {
private static final ReentrantLock lock = new ReentrantLock();
private static volatile int count = 0;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
while (getCount("thread1") < 10) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(() -> {
while(getCount("thread2") < 10) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread1.start();
thread2.start();
}
private static int getCount(String threadName) {
try {
lock.lock();
count++;
System.out.println(threadName + "得到锁" + count);
return count;
} finally {
lock.unlock();
}
}
}
输出:
thread1得到锁1
thread2得到锁2
thread1得到锁3
thread2得到锁4
thread2得到锁5
thread1得到锁6
thread1得到锁7
thread2得到锁8
thread2得到锁9
thread1得到锁10
thread2得到锁11
2. 使用ReentrantLock和condition唤醒线程
- 子线程获取锁,调用condition.await()时会释放锁并进入轮询状态(类似synchronized和object.wait()但是并不阻塞)。
- 当主线程调用condition.signal()后,并未立即释放锁,最终在主线程释放锁之后,轮询中子线程重新获得锁并在完成输出后再次释放锁。
public class ReentrantLockTest {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Condition condition = lock.newCondition();
new Thread(() -> {
try {
System.out.println("子线程等待获取lock,当前持有锁线程数:" + lock.getHoldCount());
lock.lock();
System.out.println("子线程await开始,当前持有锁线程数:" + lock.getHoldCount());
condition.await();
System.out.println("子线程await结束,当前持有锁线程数:" + lock.getHoldCount());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println("main线程等待获取lock,当前持有锁线程数:" + lock.getHoldCount());
lock.lock();
System.out.println("main线程获取lock成功,signal开始,当前持有锁线程数:" + lock.getHoldCount());
condition.signal();
System.out.println("main线程signal结束,当前持有锁线程数:" + lock.getHoldCount());
} finally {
lock.unlock();
}
}
}
输出:
子线程等待获取lock,当前持有锁线程数:0
子线程await开始,当前持有锁线程数:1
main线程等待获取lock,当前持有锁线程数:0
main线程获取lock成功,signal开始,当前持有锁线程数:1
main线程signal结束,当前持有锁线程数:1
子线程await结束,当前持有锁线程数:1
3. 使用ReentrantLock和condition实现简单多主题队列
- 主线程创建两个condition,两个子线程分别await两个condition;
- 主线程随机调用condition通知子线程进行消息处理。
public class ReentrantLockTest {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Thread thread1 = new Thread(() -> consumer(condition1, "condition1"));
thread1.setName("线程1");
Thread thread2 = new Thread(() -> consumer(condition2, "condition2"));
thread2.setName("线程2");
thread1.start();
thread2.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
try {
lock.lock();
if (Math.random() < 0.5) {
System.out.print(i + "\t激活condition1消费者 → ");
condition1.signal();
} else {
System.out.print(i + "\t激活condition2消费者 → ");
condition2.signal();
}
} finally {
lock.unlock();
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static void consumer(Condition condition, String msg) {
while (true) {
try {
lock.lock();
condition.await();
System.out.println("\t" + Thread.currentThread().getName() + "处理" + msg + "消息");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
输出:
0 激活condition1消费者 → 线程1处理condition1消息
1 激活condition2消费者 → 线程2处理condition2消息
2 激活condition2消费者 → 线程2处理condition2消息
3 激活condition1消费者 → 线程1处理condition1消息
4 激活condition1消费者 → 线程1处理condition1消息
5 激活condition1消费者 → 线程1处理condition1消息
6 激活condition1消费者 → 线程1处理condition1消息
7 激活condition1消费者 → 线程1处理condition1消息
8 激活condition2消费者 → 线程2处理condition2消息
9 激活condition2消费者 → 线程2处理condition2消息
欢迎扫码关注我微信公众号:magicTan。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。