java高并发系列 - 第13天:JUC中的Condition对象

1563532190829,t2获取锁成功!

1563532190829,t2 signal!

1563532195829,t2准备释放锁!

1563532195829,t2释放锁成功!

1563532195829,t1释放锁成功!

输出的结果和使用synchronized关键字的实例类似。

Condition.await()方法和Object.wait()方法类似,当使用Condition.await()方法时,需要先获取Condition对象关联的ReentrantLock的锁,在Condition.await()方法被调用时,当前线程会释放这个锁,并且当前线程会进行等待(处于阻塞状态)。在signal()方法被调用后,系统会从Condition对象的等待队列中唤醒一个线程,一旦线程被唤醒,被唤醒的线程会尝试重新获取锁,一旦获取成功,就可以继续执行了。因此,在signal被调用后,一般需要释放相关的锁,让给其他被唤醒的线程,让他可以继续执行。

Condition常用方法


Condition接口提供的常用方法有:

和Object中wait类似的方法

  1. void await() throws InterruptedException:当前线程进入等待状态,如果其他线程调用condition的signal或者signalAll方法并且当前线程获取Lock从await方法返回,如果在等待状态中被中断会抛出被中断异常;

  2. long awaitNanos(long nanosTimeout):当前线程进入等待状态直到被通知,中断或者超时

  3. boolean await(long time, TimeUnit unit) throws InterruptedException:同第二种,支持自定义时间单位,false:表示方法超时之后自动返回的,true:表示等待还未超时时,await方法就返回了(超时之前,被其他线程唤醒了)

  4. boolean awaitUntil(Date deadline) throws InterruptedException:当前线程进入等待状态直到被通知,中断或者到了某个时间

  5. void awaitUninterruptibly();:当前线程进入等待状态,不会响应线程中断操作,只能通过唤醒的方式让线程继续

和Object的notify/notifyAll类似的方法

  1. void signal():唤醒一个等待在condition上的线程,将该线程从等待队列中转移到同步队列中,如果在同步队列中能够竞争到Lock则可以从等待方法中返回。

  2. void signalAll():与1的区别在于能够唤醒所有等待在condition上的线程

Condition.await()过程中被打断


package com.itsoku.chat09;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

/**

  • 微信公众号:路人甲Java,专注于java技术分享(带你玩转 爬虫、分布式事务、异步消息服务、任务调度、分库分表、大数据等),喜欢请关注!

*/

public class Demo4 {

static ReentrantLock lock = new ReentrantLock();

static Condition condition = lock.newCondition();

public static class T1 extends Thread {

@Override

public void run() {

lock.lock();

try {

condition.await();

} catch (InterruptedException e) {

System.out.println(“中断标志:” + this.isInterrupted());

e.printStackTrace();

} finally {

lock.unlock();

}

}

}

public static void main(String[] args) throws InterruptedException {

T1 t1 = new T1();

t1.setName(“t1”);

t1.start();

TimeUnit.SECONDS.sleep(2);

//给t1线程发送中断信号

System.out.println(“1、t1中断标志:” + t1.isInterrupted());

t1.interrupt();

System.out.println(“2、t1中断标志:” + t1.isInterrupted());

}

}

输出:

1、t1中断标志:false

2、t1中断标志:true

中断标志:false

java.lang.InterruptedException

at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)

at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)

at com.itsoku.chat09.Demo4$T1.run(Demo4.java:19)

调用condition.await()之后,线程进入阻塞中,调用t1.interrupt(),给t1线程发送中断信号,await()方法内部会检测到线程中断信号,然后触发 InterruptedException异常,线程中断标志被清除。从输出结果中可以看出,线程t1中断标志的变换过程:false->true->false

await(long time, TimeUnit unit)超时之后自动返回


package com.itsoku.chat09;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

/**

  • 微信公众号:路人甲Java,专注于java技术分享(带你玩转 爬虫、分布式事务、异步消息服务、任务调度、分库分表、大数据等),喜欢请关注!

*/

public class Demo5 {

static ReentrantLock lock = new ReentrantLock();

static Condition condition = lock.newCondition();

public static class T1 extends Thread {

@Override

public void run() {

lock.lock();

try {

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “,start”);

boolean r = condition.await(2, TimeUnit.SECONDS);

System.out.println®;

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “,end”);

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

lock.unlock();

}

}

}

public static void main(String[] args) throws InterruptedException {

T1 t1 = new T1();

t1.setName(“t1”);

t1.start();

}

}

输出:

1563541624082,t1,start

false

1563541626085,t1,end

t1线程等待2秒之后,自动返回继续执行,最后await方法返回false,await返回false表示超时之后自动返回

await(long time, TimeUnit unit)超时之前被唤醒


package com.itsoku.chat09;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

/**

  • 微信公众号:路人甲Java,专注于java技术分享(带你玩转 爬虫、分布式事务、异步消息服务、任务调度、分库分表、大数据等),喜欢请关注!

*/

public class Demo6 {

static ReentrantLock lock = new ReentrantLock();

static Condition condition = lock.newCondition();

public static class T1 extends Thread {

@Override

public void run() {

lock.lock();

try {

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “,start”);

boolean r = condition.await(5, TimeUnit.SECONDS);

System.out.println®;

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “,end”);

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

lock.unlock();

}

}

}

public static void main(String[] args) throws InterruptedException {

T1 t1 = new T1();

t1.setName(“t1”);

t1.start();

//休眠1秒之后,唤醒t1线程

TimeUnit.SECONDS.sleep(1);

lock.lock();

try {

condition.signal();

} finally {

lock.unlock();

}

}

}

输出:

1563542046046,t1,start

true

1563542047048,t1,end

t1线程中调用 condition.await(5,TimeUnit.SECONDS);方法会释放锁,等待5秒,主线程休眠1秒,然后获取锁,之后调用signal()方法唤醒t1,输出结果中发现await后过了1秒(1、3行输出结果的时间差),await方法就返回了,并且返回值是true。true表示await方法超时之前被其他线程唤醒了。

long awaitNanos(long nanosTimeout)超时返回


package com.itsoku.chat09;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

/**

  • 微信公众号:路人甲Java,专注于java技术分享(带你玩转 爬虫、分布式事务、异步消息服务、任务调度、分库分表、大数据等),喜欢请关注!

*/

public class Demo7 {

static ReentrantLock lock = new ReentrantLock();

static Condition condition = lock.newCondition();

public static class T1 extends Thread {

@Override

public void run() {

lock.lock();

try {

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “,start”);

long r = condition.awaitNanos(TimeUnit.SECONDS.toNanos(5));

System.out.println®;

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “,end”);

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

lock.unlock();

}

}

}

public static void main(String[] args) throws InterruptedException {

T1 t1 = new T1();

t1.setName(“t1”);

t1.start();

}

}

输出:

1563542547302,t1,start

-258200

1563542552304,t1,end

awaitNanos参数为纳秒,可以调用TimeUnit中的一些方法将时间转换为纳秒。

t1调用await方法等待5秒超时返回,返回结果为负数,表示超时之后返回的。

waitNanos(long nanosTimeout)超时之前被唤醒


package com.itsoku.chat09;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

/**

  • 微信公众号:路人甲Java,专注于java技术分享(带你玩转 爬虫、分布式事务、异步消息服务、任务调度、分库分表、大数据等),喜欢请关注!

*/

public class Demo8 {

static ReentrantLock lock = new ReentrantLock();

static Condition condition = lock.newCondition();

public static class T1 extends Thread {

@Override

public void run() {

lock.lock();

try {

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “,start”);

long r = condition.awaitNanos(TimeUnit.SECONDS.toNanos(5));

System.out.println®;

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “,end”);

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

lock.unlock();

}

}

}

public static void main(String[] args) throws InterruptedException {

T1 t1 = new T1();

t1.setName(“t1”);

t1.start();

//休眠1秒之后,唤醒t1线程

TimeUnit.SECONDS.sleep(1);

lock.lock();

try {

condition.signal();

} finally {

lock.unlock();

}

}

}

输出:

1563542915991,t1,start

3999988500

1563542916992,t1,end

t1中调用await休眠5秒,主线程休眠1秒之后,调用signal()唤醒线程t1,await方法返回正数,表示返回时距离超时时间还有多久,将近4秒,返回正数表示,线程在超时之前被唤醒了。

其他几个有参的await方法和无参的await方法一样,线程调用interrupt()方法时,这些方法都会触发InterruptedException异常,并且线程的中断标志会被清除。

同一个锁支持创建多个Condition


使用两个Condition来实现一个阻塞队列的例子:

package com.itsoku.chat09;

import java.util.LinkedList;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

/**

  • 微信公众号:路人甲Java,专注于java技术分享(带你玩转 爬虫、分布式事务、异步消息服务、任务调度、分库分表、大数据等),喜欢请关注!

*/

public class BlockingQueueDemo<E> {

int size;//阻塞队列最大容量

ReentrantLock lock = new ReentrantLock();

LinkedList<E> list = new LinkedList<>();//队列底层实现

Condition notFull = lock.newCondition();//队列满时的等待条件

Condition notEmpty = lock.newCondition();//队列空时的等待条件

public BlockingQueueDemo(int size) {

this.size = size;

}

public void enqueue(E e) throws InterruptedException {

lock.lock();

try {

while (list.size() == size)//队列已满,在notFull条件上等待

notFull.await();

list.add(e);//入队:加入链表末尾

System.out.println(“入队:” + e);

notEmpty.signal(); //通知在notEmpty条件上等待的线程

} finally {

lock.unlock();

}

}

public E dequeue() throws InterruptedException {

E e;

lock.lock();

try {

while (list.size() == 0)//队列为空,在notEmpty条件上等待

notEmpty.await();

e = list.removeFirst();//出队:移除链表首元素

System.out.println(“出队:” + e);

notFull.signal();//通知在notFull条件上等待的线程

return e;

} finally {

lock.unlock();

}

}

public static void main(String[] args) {

BlockingQueueDemo<Integer> queue = new BlockingQueueDemo<>(2);

for (int i = 0; i < 10; i++) {

int data = i;

new Thread(new Runnable() {

@Override

public void run() {

try {

queue.enqueue(data);

} catch (InterruptedException e) {

}

}

}).start();

}

for (int i = 0; i < 10; i++) {

new Thread(new Runnable() {

@Override

public void run() {

try {

Integer data = queue.dequeue();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}).start();

}

}

}

代码非常容易理解,创建了一个阻塞队列,大小为3,队列满的时候,会被阻塞,等待其他线程去消费,队列中的元素被消费之后,会唤醒生产者,生产数据进入队列。上面代码将队列大小置为1,可以实现同步阻塞队列,生产1个元素之后,生产者会被阻塞,待消费者消费队列中的元素之后,生产者才能继续工作。

Object的监视器方法与Condition接口的对比


| 对比项 | Object 监视器方法 | Condition |

| — | — | — |

| 前置条件 | 获取对象的锁 | 调用Lock.lock获取锁,调用Lock.newCondition()获取Condition对象 |

| 调用方式 | 直接调用,如:object.wait() | 直接调用,如:condition.await() |

| 等待队列个数 | 一个 | 多个,使用多个condition实现 |

| 当前线程释放锁并进入等待状态 | 支持 | 支持 |

| 当前线程释放锁进入等待状态中不响应中断 | 不支持 | 支持 |

| 当前线程释放锁并进入超时等待状态 | 支持 | 支持 |

| 当前线程释放锁并进入等待状态到将来某个时间 | 不支持 | 支持 |

| 唤醒等待队列中的一个线程 | 支持 | 支持 |

| 唤醒等待队列中的全部线程 | 支持 | 支持 |

总结

  1. 使用condition的步骤:创建condition对象,获取锁,然后调用condition的方法

  2. 一个ReentrantLock支持床多个condition对象

  3. voidawait()throwsInterruptedException;方法会释放锁,让当前线程等待,支持唤醒,支持线程中断

  4. voidawaitUninterruptibly();方法会释放锁,让当前线程等待,支持唤醒,不支持线程中断

  5. longawaitNanos(longnanosTimeout)throwsInterruptedException;参数为纳秒,此方法会释放锁,让当前线程等待,支持唤醒,支持中断。超时之后返回的,结果为负数;超时之前被唤醒返回的,结果为正数(表示返回时距离超时时间相差的纳秒数)

  6. booleanawait(longtime,TimeUnitunit)throwsInterruptedException;方法会释放锁,让当前线程等待,支持唤醒,支持中断。超时之后返回的,结果为false;超时之前被唤醒返回的,结果为true

  7. booleanawaitUntil(Datedeadline)throwsInterruptedException;参数表示超时的截止时间点,方法会释放锁,让当前线程等待,支持唤醒,支持中断。超时之后返回的,结果为false;超时之前被唤醒返回的,结果为true

  8. voidsignal();会唤醒一个等待中的线程,然后被唤醒的线程会被加入同步队列,去尝试获取锁

  9. voidsignalAll();会唤醒所有等待中的线程,将所有等待中的线程加入同步队列,然后去尝试获取锁

码子不易,感觉还可以的,帮忙分享一下,谢谢!

java高并发系列目录:


1.java高并发系列 - 第1天:必须知道的几个概念

2.java高并发系列 - 第2天:并发级别

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

《MySql面试专题》

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

《MySql性能优化的21个最佳实践》

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

《MySQL高级知识笔记》

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

文中展示的资料包括:**《MySql思维导图》《MySql核心笔记》《MySql调优笔记》《MySql面试专题》《MySql性能优化的21个最佳实践》《MySq高级知识笔记》**如下图

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

关注我,点赞本文给更多有需要的人
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

[外链图片转存中…(img-Kk9Ekvjg-1713498911573)]

[外链图片转存中…(img-jqAUyMrS-1713498911574)]

[外链图片转存中…(img-5EWg6BfM-1713498911576)]

[外链图片转存中…(img-gHXcZzMy-1713498911577)]

《MySQL高级知识笔记》

[外链图片转存中…(img-BUVBmBfj-1713498911579)]

[外链图片转存中…(img-Y9mXysNj-1713498911580)]

[外链图片转存中…(img-Mq8lUZVQ-1713498911582)]

[外链图片转存中…(img-oyRiwZ7O-1713498911583)]

[外链图片转存中…(img-4fUrb6XR-1713498911585)]

[外链图片转存中…(img-ObuOdkDD-1713498911587)]

[外链图片转存中…(img-UdnshbGT-1713498911590)]

[外链图片转存中…(img-qbD0qrxN-1713498911592)]

[外链图片转存中…(img-yYM6yeB6-1713498911594)]

[外链图片转存中…(img-E8YGQOxT-1713498911595)]

文中展示的资料包括:**《MySql思维导图》《MySql核心笔记》《MySql调优笔记》《MySql面试专题》《MySql性能优化的21个最佳实践》《MySq高级知识笔记》**如下图

[外链图片转存中…(img-DFdfU5qz-1713498911596)]

关注我,点赞本文给更多有需要的人
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 29
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值