美文网首页
【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()进行唤醒时,一定要持有对应的锁才能调用该方法,直接调用该方法会抛异常。

    相关文章

      网友评论

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

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