如何理解Java中的Wait和Sleep?

本文主要介绍Java中关于线程同步的一些基本概念和方法,包括sleep()和wait()的用法和区别。

首先,Java中的wait和sleep都是用来控制线程的执行的方法,但是它们的使用场景和底层实现有很大的区别。

sleep

sleep的作用是让线程休眠指定时间,然后重新进入就绪状态。sleep可以在任何地方使用,不需要获取锁。sleep的使用场景主要是控制线程执行的时间,比如实现定时任务。

public static void sleep(long millis) throws InterruptedException {
    Thread.sleep(millis);
}

wait

wait的作用是让线程等待,直到其他线程调用notify或notifyAll方法,使该线程重新进入就绪状态,唤醒它,或者等待时间结束。wait通常和synchronized关键字一起使用,用于线程间的协调和通信。wait必须在synchronized块中调用,否则会抛出IllegalMonitorStateException异常。wait的使用场景主要是在线程间进行通信,等待某个条件的满足。

public static void wait(long millis) throws InterruptedException {
    Thread.sleep(millis);
    synchronized (this) {
        while (!isDone) {
            wait(0);
        }
    }
}

区别

两个方法的区别在于,sleep()方法会释放线程的锁,而wait()方法不会。因此,当一个线程想要等待另一个线程完成某个操作时,应该使用wait()方法,而当一个线程想要暂停一段时间时,应该使用sleep()方法。

存在的问题

wait和sleep方法都存在一些问题:

  • wait方法容易造成死锁。如果一个线程在等待另一个线程唤醒它,而另一个线程正好在等待它释放锁,就会出现死锁。
  • sleep方法不能保证精确的休眠时间,因为线程进入休眠状态后,可能会被其他线程唤醒或者中断。
  • wait和sleep方法都会使线程进入阻塞状态,如果不恰当地使用,容易导致线程饥饿、死锁等问题。

至于如何高效地使用阻塞, 更多可以参考下方的拓展阅读,比如说使用TimeUnit.SECONDS.sleep("你的暂停时间");

或者利用组件ScheduledExecutorService来实现定时操作。

ScheduledExecutorService

ScheduledExecutorService是Java中的一个线程池,用于执行定时任务或周期性任务。它继承了ExecutorService接口,可以使用submit()方法提交任务,并使用get()方法获取任务的Future对象,以便获取任务的执行结果。

我们以代码实例进行说明,比方说我们要实现一个轮询操作

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledExecutorServiceExample {

    public static void main(String[] args) {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);

        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("Task executed");
            }
        };

        executorService.scheduleAtFixedRate(task, 5, 10, TimeUnit.SECONDS);
    }
}

上述代码创建了一个ScheduledExecutorService对象,并使用scheduleAtFixedRate()方法提交了一个定时任务。该任务每隔10秒钟执行一次,一共执行5次。

在任务执行时,我们打印了一条消息,表示任务已经被执行。 需要注意的是,ScheduledExecutorService中的任务是线程安全的,因此可以在多个线程之间共享同一个ScheduledExecutorService对象。此外,ScheduledExecutorService还支持延迟时间和周期执行任务的方式,可以根据实际需求选择合适的方式来提交任务。

另外,虽然这个Service可以实现多线程共享,但是在线程较多时难免会产生竞态条件,此时我们可以为每个线程创建一个独立的Service对象。


参考资料

Java并发常见面试题总结(上) | JavaGuide(Java面试 + 学习指南)

wait - How do I make a delay in Java? - Stack Overflow

你可能感兴趣的:(面试,Java,java,开发语言)