Java多线Condition条件变量正确使用方法详解

 更新时间:2023年09月24日 09:43:46   作者:呆小鱼LQ  
这篇文章主要为大家,介绍了Java多线Condition条件变量正确使用方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

本篇文章的代码示例已放到 github 上,Git地址为:advance(记录每一个学习过程),大家在项目介绍的引用目录里面即可找到对应文章的一个代码路径。

大家有任何问题,欢迎大家在评论区留言,我会在看到后一一进行回复。

背景

在介绍 Condtion 的使用场景之前,我们先来考虑这样的场景:

当我们在执行某个方法之前,我们获得了这个方法的锁,但是在执行过程中我们发现某个条件不满足,想让方法暂停一会儿,等条件满足后再让这个方法继续执行。

针对上面的问题,我们可以利用Object.wait()和notify()写出下面这样的代码:

public synchronized void doSomething(){
    //执行方法
    if(条件不满足){
        //线程等待
        Object.wait();
    }
    //条件此时满足,对象被唤醒,继续执行方法
}

但是,由于Object.wait()和notify()过于底层,并且无法区分是由于等待超时后唤醒还是被其他线程唤醒的问题,引入在JDK1.5后引入了java.util.concurrent.locks.Condition接口。

使用场景

Condition接口作为Object.wait()/notify()的替代品,当我们给某个方法加锁后,发现某个条件不满足,想让方法暂停一会儿,等条件满足后再让这个方法继续执行。这种时候,我们就可以使用Condition接口。

常用方法

创建一个condition实例

为了让这个锁更方便获得,实例代码里面我将这个锁设为静态的

//定义一个锁
public static final Lock reentrantLock = new ReentrantLock();
//定义属于这个锁的条件变量
public static final Condition condition = reentrantLock.newCondition();

线程等待

void await() throws InterruptedException;

线程非阻塞等待

boolean await(long time, TimeUnit unit)

唤醒某个线程

condition.signal();

唤醒所有线程

condition.signalAll();

使用示例

定义一个全局的标志位

public class GlobalSymbol {
    /**
     * 定义全局标志位
     */
    public static AtomicBoolean globalFlag = new AtomicBoolean(false);
}

主线程

public class Main {
    //定义一个锁
    public static final Lock reentrantLock = new ReentrantLock();
    //定义属于这个锁的条件变量
    public static final Condition condition = reentrantLock.newCondition();
    public static void main(String[] args) {
        //先启动一下线程
        Thread thread = new Thread(new OtherThread());
        thread.start();
        //先加锁
        reentrantLock.lock();
        try {
            System.out.println("线程加锁成功,正在执行相关代码");
            while (!GlobalSymbol.globalFlag.get()){
                System.out.println("现在条件还不满足,先等待");
                condition.await();
            }
            System.out.println("线程被唤醒,执行后续代码");
        }
        catch (Exception e){
            System.out.println("加锁解锁逻辑出现异常");
        }
        finally {
            //在finally中释放锁
            reentrantLock.unlock();
        }
        System.out.println("程序结束");
    }
}

子线程(用于唤醒主线程)

public class OtherThread implements Runnable{
    @Override
    public void run() {
        try {
            for (int i = 10; i > 0; i--){
                System.out.println("标志位将在" + i + "秒后置为true");
                TimeUnit.SECONDS.sleep(1);
            }
            GlobalSymbol.globalFlag.set(true);
            //对被阻塞的线程进行唤醒(必须获得对应的锁后,才能执行唤醒的操作)
            Main.reentrantLock.lock();
            Main.condition.signalAll();
            Main.reentrantLock.unlock();
        }
        catch (Exception e){
            System.out.println("线程执行失败");
        }
    }
}

运行结果

注意事项

  • 加锁操作lock()一般放在try语句的外面,且紧接着try语句;
  • 解锁操作unlock()一般放在finally语句中,避免报错后造成锁泄漏;
  • 调用signalAll()进行唤醒时,一定要持有对应的锁才能调用该方法,直接调用该方法会抛异常。

以上就是Java多线condition条件变量正确使用 方法详解的详细内容,更多关于Java多线程condition条件变量的资料请关注脚本之家其它相关文章!

相关文章

  • SpringBoot2整合Redis实现读写操作

    SpringBoot2整合Redis实现读写操作

    Redis,对于大家来说应该不陌生,是经常使用的开发技术之一。本文将结合实例代码,介绍SpringBoot2整合Redis实现读写操作,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • Java创建线程及配合使用Lambda方式

    Java创建线程及配合使用Lambda方式

    这篇文章主要介绍了Java创建线程及配合使用Lambda方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Java超详细梳理IO流的使用方法上

    Java超详细梳理IO流的使用方法上

    流(Stream)是指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道,数据源发送的数据经过这个通道到达目的地,按流向区分为输入流和输出流
    2022-04-04
  • 详解Java编程中throw和throws子句的使用方法

    详解Java编程中throw和throws子句的使用方法

    这篇文章主要介绍了详解Java编程中throw和throws子句的使用方法,是Java入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • 详解Spring Security 捕获 filter 层面异常返回我们自定义的内容

    详解Spring Security 捕获 filter 层面异常返回我们自定义的内容

    Spring 的异常会转发到 BasicErrorController 中进行异常写入,然后才会返回客户端。所以,我们可以在 BasicErrorController 对 filter异常进行捕获并处理,下面通过本文给大家介绍Spring Security 捕获 filter 层面异常,返回我们自定义的内容,感兴趣的朋友一起看看吧
    2022-05-05
  • 解决mybatis中的mapper命名问题

    解决mybatis中的mapper命名问题

    这篇文章主要介绍了解决mybatis中的mapper命名问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • springboot结合maven实现多模块打包

    springboot结合maven实现多模块打包

    本文主要介绍了springboot借助maven完成多模块打包,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • SpringBoot整合MyBatis逆向工程及 MyBatis通用Mapper实例详解

    SpringBoot整合MyBatis逆向工程及 MyBatis通用Mapper实例详解

    这篇文章主要介绍了SpringBoot整合MyBatis逆向工程及 MyBatis通用Mapper实例详解 ,需要的朋友可以参考下
    2017-09-09
  • springboot中的静态资源加载顺序优先级

    springboot中的静态资源加载顺序优先级

    这篇文章主要介绍了springboot中的静态资源加载顺序优先级,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Springmvc DispatcherServlet原理及用法解析

    Springmvc DispatcherServlet原理及用法解析

    这篇文章主要介绍了Springmvc DispatcherServlet原理及用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09

最新评论