一、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的缺点
1、synchronized的 notify() 方法一次只能唤醒一个线程,而且唤醒线程的方式是随机的,从处于等待集中随机选取一个线程唤醒。
并且是唤醒等待队列中优先级最高的线程。
2、notify()可能会导致死锁,原因是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取出来,挨个唤醒。
170万+

被折叠的 条评论
为什么被折叠?



