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

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
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天:并发级别

最后

经过日积月累, 以下是小编归纳整理的深入了解Java虚拟机文档,希望可以帮助大家过关斩将顺利通过面试。
由于整个文档比较全面,内容比较多,篇幅不允许,下面以截图方式展示 。







由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
rue**

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

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

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

java高并发系列目录:


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

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

最后

经过日积月累, 以下是小编归纳整理的深入了解Java虚拟机文档,希望可以帮助大家过关斩将顺利通过面试。
由于整个文档比较全面,内容比较多,篇幅不允许,下面以截图方式展示 。

[外链图片转存中…(img-xmo6CNzZ-1714696228372)]
[外链图片转存中…(img-03f26VAx-1714696228373)]
[外链图片转存中…(img-eGDdFhKJ-1714696228373)]
[外链图片转存中…(img-D1HaG7eZ-1714696228373)]
[外链图片转存中…(img-WDdPY783-1714696228373)]
[外链图片转存中…(img-utwYmaEo-1714696228373)]
[外链图片转存中…(img-n0QYNdp4-1714696228374)]

由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值