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

  1. 线程t1先获取锁,然后调用了wait()方法将线程置为等待状态,然后会释放lock的锁

  2. 主线程等待5秒之后,启动线程t2,t2获取到了锁,结果中1、3行时间相差5秒左右

  3. t2调用lock.notify()方法,准备将等待在lock上的线程t1唤醒,notify()方法之后又休眠了5秒,看一下输出的5、8可知,notify()方法之后,t1并不能立即被唤醒,需要等到t2将synchronized块执行完毕,释放锁之后,t1才被唤醒

  4. wait()方法和notify()方法必须放在同步块内调用(synchronized块内),否则会报错

Condition使用简介


在了解Condition之前,需要先了解一下重入锁ReentrantLock,可以移步到:java高并发系列 - 第12天JUC:ReentrantLock重入锁

任何一个java对象都天然继承于Object类,在线程间实现通信的往往会应用到Object的几个方法,比如wait()、wait(long timeout)、wait(long timeout, int nanos)与notify()、notifyAll()几个方法实现等待/通知机制,同样的, 在java Lock体系下依然会有同样的方法实现等待/通知机制。

从整体上来看Object的wait和notify/notify是与对象监视器配合完成线程间的等待/通知机制,而Condition与Lock配合完成等待通知机制,前者是java底层级别的,后者是语言级别的,具有更高的可控制性和扩展性。两者除了在使用方式上不同外,在功能特性上还是有很多的不同:

  1. Condition能够支持不响应中断,而通过使用Object方式不支持

  2. Condition能够支持多个等待队列(new 多个Condition对象),而Object方式只能支持一个

  3. Condition能够支持超时时间的设置,而Object不支持

Condition由ReentrantLock对象创建,并且可以同时创建多个,Condition接口在使用前必须先调用ReentrantLock的lock()方法获得锁,之后调用Condition接口的await()将释放锁,并且在该Condition上等待,直到有其他线程调用Condition的signal()方法唤醒线程,使用方式和wait()、notify()类似。

示例代码:

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 Demo2 {

static ReentrantLock lock = new ReentrantLock();

static Condition condition = lock.newCondition();

public static class T1 extends Thread {

@Override

public void run() {

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “准备获取锁!”);

lock.lock();

try {

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “获取锁成功!”);

condition.await();

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

lock.unlock();

}

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “释放锁成功!”);

}

}

public static class T2 extends Thread {

@Override

public void run() {

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “准备获取锁!”);

lock.lock();

try {

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “获取锁成功!”);

condition.signal();

System.out.println(System.currentTimeMillis() + “,” + this.getName() + " signal!");

try {

TimeUnit.SECONDS.sleep(5);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “准备释放锁!”);

} finally {

lock.unlock();

}

System.out.println(System.currentTimeMillis() + “,” + this.getName() + “释放锁成功!”);

}

}

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

T1 t1 = new T1();

t1.setName(“t1”);

t1.start();

TimeUnit.SECONDS.sleep(5);

T2 t2 = new T2();

t2.setName(“t2”);

t2.start();

}

}

输出:

1563532185827,t1准备获取锁!

1563532185827,t1获取锁成功!

1563532190829,t2准备获取锁!

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

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

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

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

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

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

img

总结

面试难免让人焦虑不安。经历过的人都懂的。但是如果你提前预测面试官要问你的问题并想出得体的回答方式,就会容易很多。

此外,都说“面试造火箭,工作拧螺丝”,那对于准备面试的朋友,你只需懂一个字:刷!

给我刷刷刷刷,使劲儿刷刷刷刷刷!今天既是来谈面试的,那就必须得来整点面试真题,这不花了我整28天,做了份“Java一线大厂高岗面试题解析合集:JAVA基础-中级-高级面试+SSM框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法等”

image

且除了单纯的刷题,也得需准备一本【JAVA进阶核心知识手册】:JVM、JAVA集合、JAVA多线程并发、JAVA基础、Spring 原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB、Cassandra、设计模式、负载均衡、数据库、一致性算法、JAVA算法、数据结构、加密算法、分布式缓存、Hadoop、Spark、Storm、YARN、机器学习、云计算,用来查漏补缺最好不过。

image

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
s://img-community.csdnimg.cn/images/e5c14a7895254671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

总结

面试难免让人焦虑不安。经历过的人都懂的。但是如果你提前预测面试官要问你的问题并想出得体的回答方式,就会容易很多。

此外,都说“面试造火箭,工作拧螺丝”,那对于准备面试的朋友,你只需懂一个字:刷!

给我刷刷刷刷,使劲儿刷刷刷刷刷!今天既是来谈面试的,那就必须得来整点面试真题,这不花了我整28天,做了份“Java一线大厂高岗面试题解析合集:JAVA基础-中级-高级面试+SSM框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法等”

[外链图片转存中…(img-QNXxKWog-1712635509604)]

且除了单纯的刷题,也得需准备一本【JAVA进阶核心知识手册】:JVM、JAVA集合、JAVA多线程并发、JAVA基础、Spring 原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB、Cassandra、设计模式、负载均衡、数据库、一致性算法、JAVA算法、数据结构、加密算法、分布式缓存、Hadoop、Spark、Storm、YARN、机器学习、云计算,用来查漏补缺最好不过。

[外链图片转存中…(img-kRxjz69u-1712635509604)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值