[Java并发]

Think twice, code once. / 2024-07-15 / 原文

sleep()、wait()、join()、yield()

yield

在Java中,yield() 是一个Thread类的方法,它用于暂停当前线程并允许其他线程运行。它是一种线程调度的建议,告诉调度器可以切换到其他就绪状态的线程。调用yield()方法后,线程会从运行状态转换为就绪状态,让其他线程有机会执行。

下面是一个简单的Java代码示例,展示了yield()方法的用法:

public class YieldExample implements Runnable {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
            // 调用 yield() 方法
            Thread.yield();
        }
    }

    public static void main(String[] args) {
        // 创建两个线程
        Thread thread1 = new Thread(new YieldExample(), "Thread 1");
        Thread thread2 = new Thread(new YieldExample(), "Thread 2");

        // 启动线程
        thread1.start();
        thread2.start();
    }
}

在上面的示例中,我们创建了两个线程并启动它们。每个线程都会打印从0到4的数字。在每次循环中,我们调用了yield()方法,这样两个线程就可以交替执行,让出CPU时间给其他线程。

请注意,yield()方法的调用并不能保证当前线程一定会让出CPU时间,它只是一个建议。实际执行结果取决于操作系统和线程调度器的具体实现。

join

在Java中,join() 是Thread类的一个方法,用于等待调用该方法的线程执行完毕。当一个线程调用另一个线程的 join() 方法时,调用线程将被阻塞,直到被调用的线程执行完毕。

join() 方法有以下两种常见的用法:

  1. 使用 join() 方法等待单个线程完成:
Thread thread = new Thread(new MyRunnable());
thread.start();

// 在主线程中调用 join() 方法等待 thread 线程执行完毕
try {
    thread.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}

System.out.println("thread 执行完毕");

在上面的示例中,我们创建了一个新线程并启动它。在主线程中,我们调用了join()方法等待新线程执行完毕。主线程会被阻塞,直到新线程执行完毕后才会继续执行。

  1. 使用 join() 方法等待多个线程完成:
Thread thread1 = new Thread(new MyRunnable());
Thread thread2 = new Thread(new MyRunnable());

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

// 在主线程中分别调用 join() 方法等待 thread1 和 thread2 线程执行完毕
try {
    thread1.join();
    thread2.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}

System.out.println("thread1 和 thread2 执行完毕");

在上面的示例中,我们创建了两个新线程,并启动它们。然后在主线程中分别调用两个线程的 join() 方法,以等待它们执行完毕。主线程会被阻塞,直到两个线程都执行完毕后才会继续执行。

需要注意的是,join() 方法可能会抛出 InterruptedException,因此在使用时需要处理异常。这是由于线程在等待期间可能被中断,导致 join() 方法提前返回。

使用 join() 方法可以实现线程之间的协作和同步,确保某个线程在其他线程执行完毕后再继续执行。

sleep

在Java中,sleep() 是Thread类的一个静态方法,用于暂停当前线程的执行一段指定的时间。它是一种线程睡眠的机制。

sleep() 方法有以下两种常见的用法:

  1. 使用 sleep() 方法暂停当前线程的执行:
try {
    // 暂停当前线程执行 500 毫秒(0.5 秒)
    Thread.sleep(500);
} catch (InterruptedException e) {
    e.printStackTrace();
}

System.out.println("线程睡眠后继续执行");

在上面的示例中,我们使用 sleep() 方法暂停当前线程的执行。在这个例子中,当前线程将会睡眠500毫秒(0.5秒),然后继续执行后续的代码。

需要注意的是,sleep() 方法可能会抛出 InterruptedException,因此在使用时需要处理异常。这是由于线程在睡眠期间可能被中断,导致 sleep() 方法提前返回。

  1. 使用 sleep() 方法实现定时任务:
class MyTask implements Runnable {
    public void run() {
        try {
            // 定时任务,每隔 1 秒执行一次
            while (true) {
                System.out.println("执行定时任务");
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

// 创建一个新线程执行定时任务
Thread thread = new Thread(new MyTask());
thread.start();

在上面的示例中,我们创建了一个新线程来执行定时任务。在定时任务的循环中,我们使用 sleep() 方法使线程睡眠1秒,然后再执行下一次循环。这样就可以实现定时任务的效果。

需要注意的是,sleep() 方法会暂停当前线程的执行,但不会释放锁。如果在同步代码块或同步方法中调用 sleep() 方法,其他线程仍然无法获取到该锁。因此,在使用 sleep() 方法时要注意对锁的使用,避免可能的线程竞争和死锁问题。

总结:
sleep() 方法用于暂停当前线程的执行一段指定的时间。它可以用于实现线程的延时执行、定时任务等场景。然而,需要注意处理可能抛出的 InterruptedException 异常,并避免在同步代码块或同步方法中使用 sleep() 方法导致的线程竞争和死锁问题。

wait

在Java中,wait() 是Object类的一个方法,用于使当前线程进入等待状态,直到其他线程调用相同对象上的 notify()notifyAll() 方法来唤醒等待线程。

wait() 方法有以下常见的用法:

  1. 在同步代码块中使用 wait() 方法:
synchronized (sharedObject) {
    try {
        // 当前线程进入等待状态,释放对象的锁
        sharedObject.wait();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    // 当线程被唤醒后,继续执行
    System.out.println("线程被唤醒");
}

在上面的示例中,我们在一个同步代码块中调用了 wait() 方法。当线程执行到 wait() 方法时,它会释放对象的锁并进入等待状态,直到其他线程调用相同对象上的 notify()notifyAll() 方法来唤醒等待线程。

需要注意的是,在调用 wait() 方法前必须获得对象的锁,因此通常在同步代码块或同步方法中使用 wait() 方法。

  1. 使用 wait() 方法实现线程间的协作:
class MyTask implements Runnable {
    private final Object lock;

    public MyTask(Object lock) {
        this.lock = lock;
    }

    public void run() {
        synchronized (lock) {
            try {
                System.out.println("线程开始执行任务");

                // 执行任务的一部分

                // 等待其他线程完成特定条件
                lock.wait();

                // 线程被唤醒后继续执行任务的剩余部分
                System.out.println("线程继续执行任务");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 创建一个共享的锁对象
Object lock = new Object();

// 创建一个新线程执行任务
Thread thread = new Thread(new MyTask(lock));
thread.start();

// 在适当的时机唤醒等待的线程
synchronized (lock) {
    lock.notify();
}

在上面的示例中,我们创建了一个共享的锁对象,并在一个新线程的任务中使用 wait() 方法进行等待。主线程在适当的时机调用 notify() 方法来唤醒等待的线程。

需要注意的是,wait() 方法必须在同步块中使用,并且在调用 wait() 方法前必须获得对象的锁,否则会抛出 IllegalMonitorStateException 异常。

总结:
wait() 方法用于使当前线程进入等待状态,直到其他线程调用相同对象上的 notify()notifyAll() 方法来唤醒等待线程。它常用于线程间的协作和同步。需要在同步块中使用 wait() 方法,并在调用 wait() 方法前获得对象的锁。同时,需要注意处理可能抛出的 InterruptedException 异常和使用适当的唤醒机制。