深入浅出Java多线程(九)之Sleep()详解

1,089 阅读3分钟

这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战

前言

Sleep 可以让当前线程进行休眠,有如下两个方法:

public static void sleep​(long millis) throws InterruptedException ,mills 毫秒
public static void sleep​(long millis, int nanos) throws InterruptedException ,millis 毫秒, nanos 纳秒

如果让线程休眠 3小时15分16秒132毫秒, 使用上述方法则不够优雅,可以使用 TimeUnit 这个枚举类。
需要注意:Sleep方法不会放弃 monitor 锁的所有权,会释放CPU资源。

实例

  • sleep()虽然陷入阻塞,但并不会释放锁,会等待时间耗尽,重新执行(起休眠作用)
    所以下述代码运行的结果为

    Thread-0获取到了锁
    Thread-0释放了锁
    Thread-1获取到了锁
    Thread-1释放了锁


public class SleepWait implements Runnable{
    public static void main(String[] args) {
        SleepWait sleepWait = new SleepWait();
        Thread thread1 = new Thread(sleepWait);
        Thread thread2 = new Thread(sleepWait);
        thread1.start();
        thread2.start();
    }
    @Override
    public void run() {
        syn();
    }

    private synchronized void syn() {
        System.out.println(Thread.currentThread().getName()+"获取到了锁");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"释放了锁");
    }
}
  • Lock方法同样不会使sleep释放资源,与上述同理

        @Override
        public void run() {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"获取到了锁");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
            System.out.println(Thread.currentThread().getName()+"释放了锁");
        }
    
    • wait()会使线程陷入阻塞,并且释放锁,需要notify()或者notifyAll()唤醒
  • 可以用TimeUnit.SECONDS.sleep()取代sleep(),原因:代码更优雅,时间把控更灵活;对于非法参数(小于0),sleep会抛出异常,TimeUnit.SECONDS.sleep()会忽略

  • 关于Sleep()的总结

    sleep()方法可以让线程进入Waiting状态,并且不占用CPU资源,但是并不会释放锁,直到规定的时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态。

  • 关于sleep()的常见面试问题

    wait/notify、sleep的异同(方法属于哪个对象?线程状态如何切换)

    • 相同

      • 阻塞
      • 响应中断
    • 不同

      • 同步方法:wait/notify()方法需要在同步方法(synchronize)中执行,而sleep不需要

      • 释放锁:wait()释放锁;而sleep并不释放锁

      • 指定时间:wait()可以不传参,不传默认0(永久阻塞,直至被唤醒);sleep必须指定时间

      • 所属类:wait/notify属于Object类,Sleep属于Thread类;因为java中每个对象都有一把称之为monitor监控器的锁 由于每个对象都可以上锁,这就要求在对象头中有一个用来保存锁信息的位置 这个锁是对象级别的,而非线程级别的,wait/notify/notifyAll也都是锁级别的操作, 他们的锁属于对象,所以把他们定义在Object类中最合适,因为Object类是所有对象的父类;

        因为如果把wait/notify/notifyAll方法定义在Thread类中,会带来很大的局限性 比如一个线程可能持有多把锁,以便实现相互配合的复杂逻辑,假设此时wait方法定义到Thread类中 如何实现让一个线程持有多把锁呢?又如何明确线程等待的是那把锁呢? 既然我们是让当前线程去等待某个对象的锁,自然应该通过操作对象来实现,而不是操作线程