join() 是 Thread 的方法,wait() 和 notify() 是 Object 的方法。
join()
方法是 Thread 类的一个方法,它用于等待调用该方法的线程结束执行。具体来说,调用该方法的线程会暂停执行,知道被调用的线程执行完毕。
有以下三种重载形式:
join()
:不带参数的 join() 方法会使当前线程等待被调用的线程执行完毕。join(long millis)
:使当前线程等待被调用线程执行完毕,或者等待指定的时间,超过指定时间后当前线程会继续执行。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() 方法可以用来实现线程间的同步和协作,确保线程的执行顺序和结果的正确性。
我们在高并发场景下经常需要操作线程的状态,那么在 Java 中如何让一个线程暂停和启动呢?
在 Java 中,可以使用 Thread 类的 suspend() 和 resume() 方法来暂停和启动一个线程。然而,这两个方法在现代 Java 编程中不在推荐使用,因为它们可能会引发一些潜在的问题,比如导致资源竞争和死锁。
推荐的替代方案是使用 wait() 和 notify() 方法来实现线程的暂停和启动。
在 Java 的 Object 类中, wait() 和 notify() 方法用于实现线程之间的通信和协作,例如:生产者-消费者问题、线程池等。
该方法使当前线程进入等待状态,直到其他线程调用相同对象上的 notify() 或 notifyAll() 方法来唤醒该线程。
有以下三种重载形式:
wait()
:使当前线程无限期等待,直到其他线程调用 notify() 或 notifyAll() 方法来唤醒该线程。wait(long timeout)
:是当前线程等待指定的毫秒数,直到其他线程调用 notify() 或 notifyAll() 方法,或者超时时间到达。wait(long timeout, int nanos)
:使当前线程等待指定的毫秒数加上纳秒数,直到其他线程调用 notify() 或 notifyAll() 方法,或者超时时间到达。该方法用于唤醒在相同对象上调用 wait() 方法而进入等待状态的线程之一。如果有多个线程等待,那么只有一个线程会被唤醒。
该方法用于唤醒在相同对象上调用 wait() 方法而进入等待状态的所有线程。
下面是一个使用 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 异常。
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