乐趣区

关于java:Java多线程如何正确使用-Conditon-条件变量

前言

本篇文章的代码示例已放到 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("线程执行失败");
        }
    }
}

运行后果

注意事项

  1. 加锁操作 lock() 个别放在 try 语句的里面,且紧接着 try 语句;
  2. 解锁操作 unlock() 个别放在 finally 语句中,防止报错后造成锁透露;
  3. 调用 signalAll() 进行唤醒时,肯定要持有对应的锁能力调用该办法,间接调用该办法会抛异样。
退出移动版