美文网首页
【Java多线程】如何正确使用 Conditon 条件变量

【Java多线程】如何正确使用 Conditon 条件变量

作者: 呆小鱼LQ | 来源:发表于2023-09-19 01:48 被阅读0次

前言

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

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

大家觉得有用的话,麻烦点个star👍再走呗!

使用背景

在介绍 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("线程执行失败");
        }
    }
}

运行结果

[图片上传失败...(image-86e9f4-1695145712644)]

注意事项

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

相关文章

  • 10.多线程并发拓展

    一、死锁1.条件: 互斥条件 请求和保持条件 不剥夺条件 环路等待条件 二、多线程并发最佳实践 使用本地变量 使用...

  • java多线程相关笔记

    变量空间开辟条件: 变量的作用域 数据类型 java内存 多线程 1.线程和进程 2.多线程的作用 随着工业的进步...

  • 条件变量

    条件变量: 条件变量本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。为什么有...

  • 2.安全性

    java中多线程同步包括: synchronized 显示锁 volatile 原子变量 之所以要使用同步,是因为...

  • Java中Lock接口介绍

    在java多线程编程中,我们经常使用synchronized关键字来实现同步,控制多线程对变量的访问,来避免并发问...

  • 3.ThreadLocal源码

    ThreadLocal 属于java.lang,提供线程局部变量,用来处理多线程并发的。 使用ThreadLoca...

  • 并发:AtomicInteger 源码

    Java中的AtomicInteger大家应该很熟悉,它是为了解决多线程访问Integer变量导致结果不正确所设计...

  • Java 线程(3)- 线程共享

    当使用多线程时,当多个线程同时操作同一个变量时,由于竞争条件(race condition)可能破坏该变量的状态,...

  • JNI相关知识点

    1.生成JNI头文件 javac -h . Test.java 注意正确配置Java环境变量可使用javac -...

  • java多线程 操作共享变量 保证原子性

    java多线程 操作共享变量 保证原子性 背景:使用多线程统计所有员工每月考勤数据,第一次迟到警告,之后按照规则扣...

网友评论

      本文标题:【Java多线程】如何正确使用 Conditon 条件变量

      本文链接:https://www.haomeiwen.com/subject/mkrnvdtx.html