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

目录

    • 一、join() 用法
    • 二、wait() 和 notify() 用法
      • 1.背景
      • 2.wait() 方法
      • 3.notify() 方法
      • 4.notifyAll() 方法
      • 5.实现线程的暂停和启动
      • 6.java.lang.IllegalMonitorStateException 异常

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

你可能感兴趣的:(#,线程与线程池,java,jvm,算法)