ReentrantLock通过Condition实现锁对象的监视器功能

一、Condition

其中,Lock替代了 synchronized 方法和语句的使用,【Condition】 替代了 Object 【监视器】方法的使用

我们以往开发,需要用到锁, 并且要用到睡眠和唤醒就只有synchronized, 但是【ReentrantLock】也实现了,Lock实现了锁,Condition还实现了睡眠和唤醒的功能。
ConditionObject主要是为并发编程中的同步提供了等待通知的实现方式,可以在不满足某个条件的时候挂起线程等待。直到满足某个条件的时候在唤醒线程。

1、Condition的执行流程

在这里插入图片描述
Aqs可以创建多个condition,这多个Condition是互不影响的,主要是为了提高AQS整体的并发能力,一般我们都只会创建一个Condition对象,每个Condition关联一个链表。

condition有2个常用的方法
在这里插入图片描述

await()方法,释放锁,并且进到【条件等待】队列,等待唤醒。
signal() 或者 signalAll() 唤醒线程, 将其从【等待队列】移到【阻塞队列】。

2、synchronized的缺点

1synchronizednotify() 方法一次只能唤醒一个线程,而且唤醒线程的方式是随机的,从处于等待集中随机选取一个线程唤醒。
并且是唤醒等待队列中优先级最高的线程。
2notify()可能会导致死锁,原因是notify唤醒了线程A,可能要获取的锁此时已经被别的线程获取了,那么线程A一直在阻塞,而线程A获得锁,这个锁对应的阻塞队列里面的其他线程永远得不到执行。
(tryLock() 方法来尝试获取锁而不是直接使用lock()方法,一定程度上,避免死锁的问题。)

正确的使用场景是WaitSet中等待的条件是相同, 确保唤醒任意一个都能够执行后面的事项。 
如果被唤醒的线程无法正确处理,务必继续notify()下一个线程,并且自身要回到WaitSet等待集中。

那就要用到下面2个方法,他们可以控制具体线程的唤醒和阻塞,但是不释放锁。
LockSupport.park(this) 阻塞,但是不释放锁。
LockSupport.unpark(thread对象)  唤醒指定的线程。
与之对应的还有下面的2个方法
Thread.sleep(n) 不会释放占有的锁,但是会让出cpu执行权,到时自动唤醒,然后抢cpu的执行权。

Object.wait(n) 会释放占有的锁,没传时间就一直睡眠,需要其他线程调用Object对象notify方法。
加了时间自动醒,进到阻塞队列再来重新获取锁。


object.wait和thread.wait区别
每个类都继承了Object类,Object有个非静态的方法wait,所以要调用wait必须先有实例对象,才能调用wait方法。
Thread类也继承了Object类。
只有object.wait这么调用的,没有见过thread.wait,因为调用wait方法,必须要持有这个对象的锁,不然会报错,我们要拿到一个对象的锁,
就要使用synchronized来锁住对象。 我们也不会把线程对象thread当做我们的并发锁,

三、synchronized加锁原理

每个对象都关联了一个监视器,线程可以对其进行【加锁和解锁】操作。在同一时间,只有一个线程可以拿到对象上的监视器锁。
如果其他线程在锁被占用期间试图去获取锁,那么将会【被阻塞】直到成功获取到锁。
同时,【监视器锁可以重入】,也就是说如果线程 T 拿到了锁,那么线程T可以在解锁之前重复获取锁;每次解锁操作会反转一次加锁产生的效果


synchronized 代码块。synchronized(object) 在对某个对象上执行加锁时,会尝试在该对象的监视器上进行加锁操作,
只有成功获取锁之后,线程才会继续往下执行。线程获取到了监视器锁后,将继续执行 synchronized 代码块中的代码,如果代码块执行完成,
或者抛出了异常,线程将会自动对该对象上的【监视器执行解锁】操作。

synchronized 作用于方法,称为同步方法。同步方法被调用时,会自动执行加锁操作,只有加锁成功,方法体才会得到执行。
如果被 synchronized 修饰的方法是实例方法,那么这个实例的监视器会被锁定。
如果是 static 方法,线程会锁住相应的 Class 对象的监视器。
方法体执行完成或者异常退出后,会自动执行解锁操作。

小知识点:对 【Class】加锁、对【对象】加锁,它们之间不构成同步。synchronized 作用于静态方法时是对 Class 对象加锁,作用于实例方法时是对实例加锁。
面试中经常会问到一个类中的两个 synchronized static 方法之间是否构成同步?构成同步。

【等待状态】是指进程由于某种条件不具备,在等待队列中排队,等待cpu对它进行调度。
【阻塞状态】可能是由于竞争共享资源的情况下,没有得到锁或信号量的条件不满足而在临界区之外被暂停执行了。

文章地址

Condition使用案例

CountDownLatch主要功能,是一个计数器,主要countDown()wait()方法

1、一开始创建CountDownLatch对象,需要指定一个数字,这个数字和公平锁里面的state一样,比如100,就表示某个锁被重入了100次,
所以需要释放一百次,countDown就类似于释放一次锁的意思,所以countDown方法,要放在子线程里面执行,子线程完成一次任务,就减一。

2、什么场景要调用wait()呢,比如启动服务,需要启动创建N个组件,创建了一个组件就countDown(),我们只需要在线程池外面wait就可以了,
就等status变成0就可以了,如果没有变成0,那么调用wait的线程就会【阻塞】,并且进到阻塞队列,为什么会进到阻塞队列,
因为假如这个CountDownLatch对象是多个线程共享,那么多个线程都想知道status什么时候变成0,所以只要是通过CountDownLatch调用wait那
么就会阻塞,那么多个线程阻塞总得排队,多个线程调用wait都有可能阻塞进到队列,那么什么时候唤醒这些阻塞队列,就是等state变成0,
然后通过自旋的方式,从阻塞队列里面把所有Node取出来,挨个唤醒。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

信仰_273993243

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值