ReentrantLock
ReentrantLock
学习ReentrantLock之前建议去看,Java锁这些基础知识和synchronized的原理。当然有操作系统的基础就更好了。
ReentantLock 实现接口 Lock ,并实现了接口中定义的方法,他是一种可重入锁,除了能完成 synchronized 所能完成的所有工作外,还提供了: 可响应中断锁、可轮询锁请求、定时锁等避免多线程死锁的方法。ReentrantLock故名思意就是可重入锁。
Lock接口实现方法
- void lock(): 执行此方法时, 如果锁处于空闲状态, 当前线程将获取到锁.
- boolean tryLock():如果锁可用, 则获取锁, 并立即返回 true, 否则返回 false.
- tryLock(long timeout TimeUnit unit):如果锁在给定等待时间内没有被另一个线程保持,则获取该锁。
- void unlock():执行此方法时, 当前线程将释放持有的锁.
- Condition newCondition():条件对象,获取等待通知组件。
- getHoldCount() :查询当前线程保持此锁的次数,也就是执行此线程执行 lock 方法的次数。
- getQueueLength():返回正等待获取此锁的线程估计数,比如启动 10 个线程,1 个线程获得锁,此时返回的是 9
- getWaitQueueLength:(Condition condition)返回等待与此锁相关的给定条件的线程估计数。
- hasQueuedThread(Thread thread):查询给定线程是否等待获取此锁
- isFair():该锁是否公平锁
- isHeldByCurrentThread(): 当前线程是否保持锁锁定,线程的执行 lock 方法的前后分别是 false 和 true
- isLock():此锁是否有任意线程占用
- lockInterruptibly():如果当前线程未被中断,获取锁
ReentrantLock构造方法
ReentrantLock默认是非公平锁的。ReentrantLock是可重入锁,也可以根据构造方法,设置为公平的或非公平的。公平锁:先来后到,非公平锁:允许尝试插队,插队不不成功就排队。
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock 与 synchronized
- ReentrantLock 通过方法 lock()与 unlock()来进行加锁与解锁操作,与 synchronized 会 被 JVM 自动解锁机制不同,ReentrantLock 加锁后需要手动进行解锁。
- ReentrantLock 相比 synchronized 的优势是可中断、公平锁、多个锁。
- synchronized是Java关键字,ReentrantLock的Java类
- ReentrantLock的自由度比synchronized高,可中断,可公平锁、多个锁
Condition类
Condition用来代替Object 类wait 方法和notify 方法等,两者可以等价。
- Condition 类的 awiat 方法和 Object 类的 wait 方法等效
- Condition 类的 signal 方法和 Object 类的 notify 方法等效
- Condition 类的 signalAll 方法和 Object 类的 notifyAll 方法等效
- ReentrantLock 类可以唤醒指定条件的线程,而 object 的唤醒是随机的
Condition类和synchronized传统的唤醒方式对比:
public class testLock {
private int num = 0;
Lock lock = new ReentrantLock();
Condition cdt = lock.newCondition();
public synchronized void increment() throws InterruptedException {
while (num != 0 ) //如果有东西就等待
this.wait();
//增加
num++;
System.out.println(Thread.currentThread().getName()+"=>"+num);
//操作完毕通知其他线程
this.notify();
}
public synchronized void decrement() throws InterruptedException {
while (num == 0 ) //没东西等待
this.wait();
//减小
num--;
System.out.println(Thread.currentThread().getName()+"=>"+num);
//操作完毕通知其他线程
this.notify();
}
public void increment2() throws InterruptedException {
lock.lock();
while (num != 0 ) //如果有东西就等待
cdt.await();
//增加
num++;
System.out.println(Thread.currentThread().getName()+"=>"+num);
//操作完毕通知其他线程
cdt.signal();
lock.unlock();
}
public void decrement2() throws InterruptedException {
lock.lock();
while (num == 0 ) //没东西等待
cdt.await();
//减小
num--;
System.out.println(Thread.currentThread().getName()+"=>"+num);
//操作完毕通知其他线程
cdt.signal();
lock.unlock();
}
public static void main(String[] args) {
testLock test = new testLock();
/*
new Thread(()->{
try {
for(int i = 0;i<10;i++)
test.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
for(int i = 0;i<10;i++)
test.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
*/
new Thread(()->{
try {
for(int i = 0;i<10;i++)
test.increment2();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"C").start();
new Thread(()->{
try {
for(int i = 0;i<10;i++)
test.decrement2();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"D").start();
}
}
还有些基础知识:awiat 方法会释放线程持有的锁,至于为什么await()后面还要加unlock(),那是因为当线程被其他线程唤醒时,会获得锁,所以用完后还是需要unlock()。
无非就下面两种情况:
- lock()获得锁—>unlock()释放锁
- lock()获得锁—>awiat()等待并释放锁—>被其他线程signal()唤醒获得锁—>unlock()释放锁
至于调不调用awiat()完全看当前拿到锁的线程十分有条件完成下一步操作。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)