admin管理员组文章数量:1599543
AQS中的ReentrantLock.lock可以对应理解成synchronized刚进入代码块获取到锁
AQS中的ReentrantLock.unlock可以对应理解成synchronized代码块结束释放锁
Condition condition = reentrantLock.newCondition()
condition.await 可以理解成 Object.wait 方法的封装
condition.signal 可以理解成 Object.notify 方法的封装
先看一个问题:
一个线程调用获取锁Lock.lock()方法后会停止运行,一个线程调用wait()方法也会停止运行,区别是什么呢?
区别
- 前者停止运行是阻塞状态blocked,后者的停止运行是等待waiting
(tip:其实Lock.lock()这里举例子不准确,因为底层其内部调用的是LockSupport.park(),在Thread#State#WAITING明确说了这是一个等待状态,所以这里注意一下概念即可) - lock的作用是保护多线程访问的共享资源,而wait的作用是用于多线程之间的线程通信,作用不一样
- 线程之间的通信方法Condition.await 和 Condition.signal (Object.wait 和 Object.notify) 的调用前提是必须获取到锁,如果没有获取到锁就直接调用,则会抛出java.lang.IllegalMonitorStateException 异常
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition1 = reentrantLock.newCondition();
public void fun1() {
condition1.await(); // 直接抛出 java.lang.IllegalMonitorStateException 异常
//调用 condition1.signal 同样抛出 java.lang.IllegalMonitorStateException 异常
}
public void fun2() {
reentrantLock.lock();
condition1.await(); // 不会抛出异常,因为获取到了锁
// condition1.signal() 不会抛出异常
... finally 释放锁
}
- A线程调用 Lock.lock() 导致线程阻塞,如果B线程调用 Lock.unlock() 则自动会解除A线程的阻塞状态,该机制是JVM调度器来唤醒,不需要B线程显示的唤醒A
public void fun1() {
// 线程A
new Thread(() -> {
reentrantLock.lock();
System.out.println("线程1获取锁成功");
TimeUnit.SECONDS.sleep(5);// try - catch
// 此处一旦释放锁,下面的线程B就会立即被唤醒和执行
reentrantLock.unlock(); //建议在finally中释放
}).start();
// 线程B
new Thread(() ->{
reentrantLock.lock();
System.out.println("线程2获取锁成功");
reentrantLock.unlock(); //建议在finally中释放
}).start();
}
- A线程调用了 Condition.await(Object.wait),如果要唤醒A线程,那么B线程必须做到:
a 获取到锁(因为A线程wait释放了锁)
b 调用 Condition.signal(Object.notify),这个时候A还不能执行,只是被唤醒,唤醒后需要重新获取锁
c 调用unlock释放锁,只有B线程释放了锁,A线程才能真正的被唤醒,然后获取到锁执行
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition1 = reentrantLock.newCondition();
@Test
public void fun3() throws InterruptedException {
// 线程A
new Thread(() ->{
reentrantLock.lock();// 线程A首先获取到锁
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {}
try {
// 然后进入等待 后续代码不会被执行
condition1.await();
} catch (InterruptedException e) {}
reentrantLock.unlock();
}).start();
// 线程B
new Thread(() ->{
// A线程调用了 condition1.await() 方法,B线程才可以获取锁
reentrantLock.lock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {}
//执行唤醒A线程,这个操作不会释放锁,A被唤醒后开始获取锁,由于锁还是在B手中,所以A无法执行
condition1.signal();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {}
//B释放锁,只要这里一执行,那么A线程就会从唤醒状态获取到锁,变为 running状态
reentrantLock.unlock();
}).start();
// 防止测试主线程提前退出
TimeUnit.DAYS.sleep(1);
}
上述区别中提到的 Condition.await(Object.wait)和 Condition.signal(Object.notify)
其实就是线程通信机制,Object的两个方法很好理解,一个是进入等待,一个唤醒等待的线程
而Condition这个类是AQS里面的ReentrantLock对象的条件类,理解成Object两个方法的封装,使用起来更方便而已。Condition.await对应Object.wait,Condition.signal对应Object.notify
Note:
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition1 = reentrantLock.newCondition();
Condition condition2 = reentrantLock.newCondition();
newCondition方法获取的条件是不同的,我们知道,Object.notify 是唤醒一个等待的线程,但是在多生产者和多消费者模式中,生产者调用 notify 方法会唤醒一个等待线程,而这个等待线程很有可能也是一个 生产者,根本就没有唤醒消费者,就会出现死循环
所以如果能将生产者和消费者使用的条件隔离开,问题就解决了
方案就是:
生产者 使用一个 condition1
消费者 使用一个 condition2
这个案例可以查看
线程通信wait() notify() notifyAll()的正确使用
本文标签: 区别waitLockReentrantLockcondition
版权声明:本文标题:锁lock和等待wait区别以及ReentrantLock-Condition使用 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dianzi/1728324252a1154225.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论