Loading

[Java并发] sleep()、wait()、join()、yield()比较

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时间,它只是一个建议。实际执行结果取决于操作系统和线程调度器的具体实现。

yield使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚调用yield那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。

join

join()是Thread类的一个成员方法。根据jdk文档的定义:

public final void join() throws InterruptedException: Waits for this thread to die.

join()方法的作用,是等待这个线程结束;但显然,这样的定义并不清晰。个人认为"Java 7 Concurrency Cookbook"的定义较为清晰:

Waiting for the finalization of a thread
In some situations, we will have to wait for the finalization of a thread. For example, we may have a program that will begin initializing the resources it needs before proceeding with the rest of the execution. We can run the initialization tasks as threads and wait for its finalization before continuing with the rest of the program. For this purpose, we can use the join() method of the Thread class. When we call this method using a thread object, it suspends the execution of the calling thread until the object called finishes its execution.

解释一下,是主线程等待子线程的终止。也就是说主线程的代码块中,如果碰到了t.join()方法,此时主线程需要等待(阻塞),等待子线程结束了(Waits for this thread to die.),才能继续执行t.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(只有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 异常和使用适当的唤醒机制。

wait()和sleep()

  1. wait会释放锁而sleep不会释放锁资源,sleep 方法只让出了CPU,没有释放同步资源锁! wait方法则是指当前线程让自己暂时退让出同步资源锁,以便其他正在等待该资源的线程得到该资源进而运行,只有调用了notify方法,之前调用wait()的线程才会解除wait状态,可以去参与竞争同步资源锁,进而得到执行。
  2. sleep调用后停止运行期间仍持有同步锁,所以到时间会继续执行;wait调用会放弃对象锁,进入等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才会进入锁池,再次获得对象锁后才会进入运行状态,在没有获取对象锁之前不会继续执行;
  3. wait只能在同步方法和同步块中使用,而sleep任何地方都可以。
  4. wait无需捕捉异常,而sleep需要。
  5. sleep() 是线程类(Thread)的方法;wait() 是顶级类 Object 的方法。
posted @ 2024-07-14 22:32  Duancf  阅读(35)  评论(0)    收藏  举报