join()、wait()、notify() 线程方法概述

本文主要介绍Java中线程相关方法。join()是Thread类方法,用于等待调用该方法的线程结束,可实现线程顺序执行与协调。wait()和notify()是Object类方法,用于线程通信协作,可实现线程暂停和启动,但使用时需在同步块中,否则会抛出IllegalMonitorStateException异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

join() 是 Thread 的方法,wait() 和 notify() 是 Object 的方法。

一、join() 用法

join() 方法是 Thread 类的一个方法,它用于等待调用该方法的线程结束执行。具体来说,调用该方法的线程会暂停执行,知道被调用的线程执行完毕。

有以下三种重载形式:

  1. join():不带参数的 join() 方法会使当前线程等待被调用的线程执行完毕
  2. join(long millis):使当前线程等待被调用线程执行完毕,或者等待指定的时间,超过指定时间后当前线程会继续执行
  3. join(long millis, int nanos):使当前线程等待被调用的线程执行完毕,或等待指定的时间,超过指定时间后当前线程会继续执行。其中,millis 标识等待时间的毫秒数,nanos 表示等待时间的纳秒数。

join() 方法常用于实现线程的顺序执行和线程之间的协调。例如,在主线程中创建了线程 A、线程 B,如果希望主线程在线程 A 和线程 B 执行完毕后再继续执行,可以在主线程中调用线程 A 和线程 B 的 join() 方法。

代码示例:

public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
        System.out.println("线程1开始执行");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程1执行完毕");
    });

    Thread thread2 = new Thread(() -> {
        System.out.println("线程2开始执行");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程2执行完毕");
    });

    thread1.start();
    thread2.start();

    try {
        thread1.join(); // 等待线程1执行完毕
        thread2.join(); // 等待线程2执行完毕
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("所有线程执行完毕");
}

执行结果:

线程1开始执行
线程2开始执行
线程2执行完毕
线程1执行完毕
所有线程执行完毕

可以看到,在调用 join() 方法后,主线程会等待线程 A 和线程 B 执行完毕后再继续执行。

总之,join() 方法可以用来实现线程间的同步和协作,确保线程的执行顺序和结果的正确性。

二、wait() 和 notify() 用法

1.背景

我们在高并发场景下经常需要操作线程的状态,那么在 Java 中如何让一个线程暂停和启动呢?

在 Java 中,可以使用 Thread 类的 suspend()resume() 方法来暂停和启动一个线程。然而,这两个方法在现代 Java 编程中不在推荐使用,因为它们可能会引发一些潜在的问题,比如导致资源竞争和死锁

推荐的替代方案是使用 wait()notify() 方法来实现线程的暂停和启动。

在 Java 的 Object 类中, wait()notify() 方法用于实现线程之间的通信和协作,例如:生产者-消费者问题、线程池等。

2.wait() 方法

该方法使当前线程进入等待状态,直到其他线程调用相同对象上的 notify() 或 notifyAll() 方法来唤醒该线程。

有以下三种重载形式:

  1. wait():使当前线程无限期等待,直到其他线程调用 notify()notifyAll() 方法来唤醒该线程。
  2. wait(long timeout):是当前线程等待指定的毫秒数,直到其他线程调用 notify()notifyAll() 方法,或者超时时间到达。
  3. wait(long timeout, int nanos):使当前线程等待指定的毫秒数加上纳秒数,直到其他线程调用 notify()notifyAll() 方法,或者超时时间到达。

3.notify() 方法

该方法用于唤醒在相同对象上调用 wait() 方法而进入等待状态的线程之一。如果有多个线程等待,那么只有一个线程会被唤醒。

4.notifyAll() 方法

该方法用于唤醒在相同对象上调用 wait() 方法而进入等待状态的所有线程。

5.实现线程的暂停和启动

下面是一个使用 wait() 和 notify() 方法来实现线程暂停和启动的示例代码:

class MyThread extends Thread {

    @Override
    public void run() {
        // do something
    }

    /**
     * 阻塞当前线程
     */
    public synchronized void getLock() {
        try {
            wait();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 唤醒当前线程
     */
    public synchronized void releaseLock() {
        notify();
    }
}

在上述示例中,MyThread 类继承了 Thread 类,通过调用 getLock()releaseLock() 方法来实现线程的暂停和启动。

需要注意的是,在使用 wait()notify() 方法时,需要在同步块(synchronized)中进行调用,并且使用共享的对象(this 或其他)作为锁,已确保线程间的正常通信和同步。

上述代码中,方法上的 synchronized 相当于:

synchronized (this) {
    // 方法体...
}

如果使用 wait()notify() 方法时,没有在同步块(synchronized)中进行调用,就会抛出:java.lang.IllegalMonitorStateException 异常。

6.java.lang.IllegalMonitorStateException 异常

java.lang.IllegalMonitorStateException 是由于没有持有锁的情况下调用了 wait()notify()notifyAll() 方法而引发的异常。

在使用 wait()notify()notifyAll() 方法之前,我们需要确保当前线程已经获取到了对象的锁。可以使用 synchronized 关键字或 ReentrantLock 类来创建同步代码块或临界区,以确保在调用这些方法时能够持有对象的锁。

下面是一个示例代码,用于演示如何正确使用 wait()notify() 方法:

class MyThread implements Runnable {
    private final Object lock = new Object();
    private boolean running = true;

    public void run() {
        synchronized (lock) {
            while (running) {
                // 执行线程逻辑
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public void pause() {
        running = false;
    }
    
    public void resume() {
        running = true;
        synchronized (lock) {
            lock.notify();
        }
    }
}

在上述代码中,我们使用了一个对象 lock 来进行同步操作。在 run() 方法中,我们在持有 lock 对象的锁的前提下调用了 wait() 方法。在 resume() 方法中,也需要在持有 lock 对象的锁的前提下调用 notify() 方法。

请确保在调用 wait()notify()notifyAll() 方法之前,已经获取到了对象的锁。否则,将会抛出 **IllegalMonitorStatementException 异常。

整理完毕,完结撒花~ 🌻





参考地址:

1.Thread之三:Thread Join()的用法,https://blog.csdn.net/bingguang1993/article/details/104070781

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不愿放下技术的小赵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值