程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

wait()和sleep()、yield()、jion()如何使用?

balukai 2024-12-26 11:40:15 文章精选 9 ℃

wait()方法

  • 定义和所属类:wait()方法是java.lang.Object类中的一个方法。这意味着在 Java 中,任何对象都可以调用这个方法。
  • 作用机制
  • 当一个线程调用一个对象的wait()方法时,它会释放该对象的锁,然后进入等待状态。这个线程会一直等待,直到其他线程调用该对象的notify()或notifyAll()方法来唤醒它。例如,在一个生产者 - 消费者模型中,消费者线程在发现缓冲区为空时,可以调用wait()方法等待生产者生产数据。当生产者生产了数据并调用notify()方法后,消费者线程就会被唤醒,重新获取对象的锁并继续执行。
  • 线程在wait()状态下是处于阻塞状态的一种,它会被放入对象的等待队列中,这个队列是和对象关联的,用于管理等待这个对象的线程。
  • 使用场景:主要用于线程之间的协作和同步。比如在多线程访问共享资源(如共享数据结构)时,通过wait()和notify()可以实现对资源访问的有效控制,避免数据竞争和不一致性。
  • 注意事项:调用wait()方法必须在同步代码块或者同步方法中,因为wait()方法会释放对象的锁,所以需要先获取锁才能释放。否则会抛出IllegalMonitorStateException异常。
  • 示例代码
import java.util.ArrayList;
import java.util.List;

class ProducerConsumerExample {
    private List<Integer> buffer = new ArrayList<>();
    private final int bufferSize = 5;

    public synchronized void produce() {
        while (buffer.size() == bufferSize) {
            try {
                // 如果缓冲区已满,生产者线程等待
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        buffer.add(1);
        System.out.println("Produced. Buffer size: " + buffer.size());
        // 通知消费者线程可以消费了
        notify();
    }

    public synchronized void consume() {
        while (buffer.isEmpty()) {
            try {
                // 如果缓冲区为空,消费者线程等待
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        buffer.remove(0);
        System.out.println("Consumed. Buffer size: " + buffer.size());
        // 通知生产者线程可以生产了
        notify();
    }
}

public class WaitExample {
    public static void main(String[] args) {
        ProducerConsumerExample example = new ProducerConsumerExample();
        Thread producerThread = new Thread(example::produce);
        Thread consumerThread = new Thread(example::consume);

        producerThread.start();
        consumerThread.start();
    }
}

在上述代码中,生产者线程在缓冲区已满时调用wait()方法等待,消费者线程在缓冲区为空时也调用wait()方法等待。当生产者生产了一个数据后,通过notify()方法通知消费者线程,当消费者消费了一个数据后,通过notify()方法通知生产者线程。

sleep()方法

  • 定义和所属类:sleep()方法是java.lang.Thread类中的一个静态方法。这意味着它是通过线程类来调用的,而不是像wait()那样通过对象来调用。
  • 作用机制
  • 当一个线程调用Thread.sleep(long millis)方法时,它会使当前线程暂停执行指定的毫秒数。在这个期间,线程进入阻塞状态,但和wait()不同的是,它不会释放对象的锁(如果线程持有锁的话)。例如,如果一个线程在同步方法中调用sleep()方法,它依然会持有该方法所属对象的锁,其他线程无法访问这个同步方法,直到sleep()时间结束。
  • 线程在sleep()结束后,会重新进入就绪状态,等待 CPU 调度,然后继续执行。
  • 使用场景:用于让线程暂停一段时间,比如模拟延迟、定时任务等。例如,一个每隔一段时间就检查系统状态的线程,可以通过sleep()方法来实现定时检查。
  • 注意事项:sleep()方法可能会抛出InterruptedException异常。这个异常通常在其他线程调用当前线程的interrupt()方法时抛出。所以在调用sleep()方法时,通常需要在try - catch块中处理这个异常。
  • 示例代码:
public class SleepExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("Thread started.");
                // 线程暂停 3 秒
                Thread.sleep(3000);
                System.out.println("Thread resumed after 3 seconds.");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        thread.start();
    }
}

在这个例子中,创建了一个线程,该线程在启动后先打印一条消息,然后调用sleep(3000)让线程暂停 3 秒,3 秒后继续执行并打印另一条消息。


yield()方法

  • 定义和所属类:yield()方法是java.lang.Thread类中的一个静态方法。
  • 作用机制
  • 当一个线程调用yield()方法时,它是在暗示调度器自己当前可以让出 CPU 资源。但是调度器是否会真正让当前线程暂停执行,这是不确定的。它可能会让当前线程暂停,然后重新进入就绪队列,等待下一次调度;也可能会忽略这个请求,让线程继续执行。
  • 线程调用yield()方法后,会从运行状态转换到就绪状态,它不会像sleep()那样进入阻塞状态,也不会释放对象的锁(如果线程持有锁的话)。
  • 使用场景:用于在多线程环境中,当一个线程完成了一部分任务,并且认为其他线程可能更需要 CPU 资源时,可以调用yield()方法来给其他线程机会执行。不过由于调度器的不确定性,它在实际应用中的效果可能不太好预测。
  • 注意事项:yield()方法是一种启发式的方法,不能保证一定能达到预期的调度效果。在不同的操作系统和 JVM 实现中,yield()方法的行为可能会有所不同。
  • 示例代码
public class YieldExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread 1: " + i);
                // 偶尔让出 CPU 资源
                if (i % 2 == 0) {
                    Thread.yield();
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread 2: " + i);
                // 偶尔让出 CPU 资源
                if (i % 3 == 0) {
                    Thread.yield();
                }
            }
        });

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

在这个例子中,创建了两个线程,它们在循环中偶尔调用yield()方法尝试让出 CPU 资源,但实际效果取决于调度器的决策。

join()方法

  • 定义和所属类:join()方法是java.lang.Thread类中的一个方法。
  • 作用机制
  • 当在一个线程中调用另一个线程的join()方法时,当前线程会被阻塞,直到被调用join()方法的线程执行完毕。例如,有线程 A 和线程 B,在 A 中调用 B.join (),那么 A 会暂停执行,等待 B 完成后 A 才会继续执行。
  • 使用场景:用于协调线程的执行顺序。比如在主线程中,如果需要等待某个子线程完成特定任务后再继续执行后续操作,就可以使用join()方法。
  • 注意事项:join()方法可能会抛出InterruptedException异常。这个异常通常在当前线程被中断时抛出。所以在调用join()方法时,通常也需要在try - catch块中处理这个异常。
  • 示例代码
public class JoinExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("Sub - thread started.");
                // 模拟子线程执行一些耗时任务
                Thread.sleep(2000);
                System.out.println("Sub - thread finished.");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        thread.start();
        try {
            System.out.println("Main thread is waiting for sub - thread to finish.");
            // 主线程等待子线程完成
            thread.join();
            System.out.println("Main thread continues after sub - thread finished.");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

在这个例子中,主线程启动一个子线程后,调用join()方法等待子线程完成。子线程在执行过程中会暂停 2 秒来模拟耗时任务,当子线程完成后,主线程才会继续执行。

异同点总结

  • 相同点
  • 状态转换:wait()、sleep()、yield()和join()方法都会导致线程状态的改变。wait()和sleep()方法会使线程进入阻塞状态(wait()是无限等待或等待被唤醒,sleep()是限时等待),yield()方法会使线程从运行状态转换到就绪状态,join()方法会使当前线程进入阻塞状态,直到被调用join()方法的线程执行完毕。
  • 影响执行顺序:它们都在一定程度上影响了线程的执行顺序。wait()是等待其他线程唤醒,sleep()是暂停一段时间后再继续执行,yield()是让出 CPU 资源(可能),join()是等待另一个线程完成,这些操作都会改变线程原本的执行顺序。
  • 不同点
  • 所属类和调用方式:wait()是Object类的方法,需要通过对象来调用;sleep()和yield()、join()是Thread类的方法,sleep()和join()是实例方法,yield()是静态方法,sleep()是通过Thread.sleep()调用,yield()是通过Thread.yield()调用,join()是通过线程对象的join()方法调用。
  • 锁的释放:wait()方法会释放对象的锁,而sleep()、yield()和join()方法不会释放对象的锁(如果线程持有锁的话)。
  • 等待性质:wait()是等待其他线程通过notify()或notifyAll()来唤醒,是一种基于对象通知的等待;sleep()是基于时间的等待,线程会在指定时间后自动恢复;yield()是一种协作式的让出 CPU 资源,调度器是否接受这个请求是不确定的;join()是等待指定线程执行完毕的等待。
最近发表
标签列表