TimeUnit类及其常见用法(详解timedWait、sleep方法)

目录

一、是什么

二、枚举类属性详细信息

三、方法详细信息

1、public long convert(long sourceDuration,TimeUnit sourceUnit)

2、public static final TimeUnit[] values()

3、public static TimeUnit valueOf(String name)

4、toXxx

5、public void timedWait(Object obj,  long timeout)  throws InterruptedException

6、public void sleep(long timeout)

7、public void timedJoin(Thread thread, long timeout)


一、是什么

    TimeUnit 表示给定单元粒度的时间段。TimeUnit是java.util.concurrent下面的一个枚举类,它提供在给定单元中进行跨单元转换和执行计时及延迟操作的实用工具方法。

二、枚举类属性详细信息

1、NANOSECONDS:纳秒(=千分之一微妙)

2、MICROSECONDS:微妙(=千分之一毫秒)

3、MILLISECONDS:毫秒(=千分之一秒)

4、SECONDS:秒

5、MINUTES:分钟

6、HOURS:小时

7、DAYS:天

三、方法详细信息

1、public long convert(long sourceDuration,TimeUnit sourceUnit)

      将给定单元的时间段转换到此单元。

      参数:sourceDuration,表示时间段;sourceUnit,表示单元。

      返回值:此单元中的转换时间段;如果转换将负溢出,则返回 Long.MIN_VALUE;如果转换将正溢出,则返回 Long.MAX_VALUE。

      例如,下面的代码将十分钟转换为毫秒。

@Test
public void test01() {
    long a = TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES);
    System.out.println("10分钟转换为毫秒为:" + a);
}

      打印结果:10分钟转换为毫秒为:600000

      convert方法有两点需要注意。

      第一点,从较细粒度到较粗粒度的舍位转换,会失去精确性。例如下面的代码,在将58分钟转换成小时的时候,结果为0,0小时显示是不对的。

@Test
public void test01() {
    long b = TimeUnit.HOURS.convert(58L, TimeUnit.MINUTES);
    System.out.println("将58分钟转换成小时:"+b);
}

      打印结果:将58分钟转换成小时:0

      第二点,使用参数从较粗粒度到较细粒度转换,如果参数为负,则在数字上溢出至 Long.MIN_VALUE,如果为正,则为 Long.MAX_VALUE。

2、public static final TimeUnit[] values()

      返回一个数组,数组中的内容TimeUnit中定义的常量。常量的值与此枚举类声明的顺序一致。

      例如,

@Test
 public void test01() {

    for (TimeUnit timeUnit : TimeUnit.values()) {
       System.out.println(timeUnit);
    }

 }

      打印结果:

      NANOSECONDS
      MICROSECONDS
      MILLISECONDS
      SECONDS
      MINUTES
      HOURS
      DAYS

3、public static TimeUnit valueOf(String name)

      返回带有指定名称的该类型的枚举常量。字符串必须与用于声明该类型的枚举常量的标识符完全匹配。(不允许有多余的空格。)

      参数:指定要返回的枚举常量的名称。

      返回:返回带有指定名称的枚举常量 

      抛出: 如果该枚举类型没有带有指定名称的常量,则抛出 IllegalArgumentException。

      例如,

    @Test
    public void test01() {

        System.out.println(TimeUnit.valueOf("SECONDS"));
        // 有空格,抛出异常
        System.out.println(TimeUnit.valueOf(" SECONDS"));

    }

      打印结果:

      SECONDS

      java.lang.IllegalArgumentException: No enum constant java.util.concurrent.TimeUnit. SECONDS

4、toXxx

(1)public long toNanos(long duration)

      将指定时间段转换成纳秒。等效于 NANOSECONDS.convert(duration, this)。

      例如,下面的代码将10微妙转换为纳秒

      long a = TimeUnit.MICROSECONDS.toNanos(10L);
      System.out.println(a);

      运行的结果为:10000

(2)public long toMicros(long duration)

      将指定时间段转换成微秒。等效于 MICROSECONDS.convert(duration, this)。

(3)public long toMillis(long duration)

      将指定时间段转换成毫秒。等效于 MILLISECONDS.convert(duration, this)。 

(4)public long toSeconds(long duration)

      将指定时间段转换成秒。等效于 SECONDS.convert(duration, this)。 

(5)public long toMinutes(long duration)

      将指定时间段转换成分钟。等效于 MINUTES.convert(duration, this)。 

(6)public long toHours(long duration)

      将指定时间段转换成小时。等效于 HOURS.convert(duration, this)。 

(7)public long toDays(long duration)

      将指定时间段转换成天。等效于 DAYS.convert(duration, this)。 

5、public void timedWait(Object obj,  long timeout)  throws InterruptedException

      使指定对象等待指定时间。

       使用此时间单元执行计时的 Object.wait(也就是内部调用Object.wait,但是好处多多)。这是将超时参数转换为 Object.wait 方法所需格式的便捷方法。 

      参数:
      obj - 要等待的对象
      timeout - 要等待的最长时间。如果小于等于 0,则根本不会等待。 
      抛出: 
      InterruptedException - 如果等待时中断。

      示例,下面的代码,使当前对象等带5秒钟。

public static void main(String[] args) {
        try {
            System.out.println("start!");
            // 使当前对象等待5秒钟
            new TimeUnitTest().poll(5L, TimeUnit.SECONDS);
            System.out.println("finish");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void poll(long timeout, TimeUnit unit) throws InterruptedException {
        unit.timedWait(this, timeout);
    }

      针对开头说的“使用此时间单元执行计时的 Object.wait。这是将超时参数转换为 Object.wait 方法所需格式的便捷方法。 ”。

      什么意思呢?就是说,我们不用直接去调用Object类里的wait方法,而是通过TimeUnit的timedWait方法来调用。为什么要这么做呢?

      我们通过一个例子来说明。

      如下代码所示。首先,mian方法里调用poll方法,参数如下图。

TimeUnit类及其常见用法(详解timedWait、sleep方法)_第1张图片

      然后,poll方法里调用timedWait方法。

TimeUnit类及其常见用法(详解timedWait、sleep方法)_第2张图片     

      然后,进入timedWait方法内部,我们看到参数发生了如下变化。

TimeUnit类及其常见用法(详解timedWait、sleep方法)_第3张图片

      到这里,大家可能会有一个疑问,为什么要将传过来的时间作毫秒和纳秒处理呢?     

      如果你有这个疑问,说明你认真看了演示的流程,真是个爱学习的好孩子。

      既然有这个疑问,我们就继续往下看。

      现在,我们进入到Object的wait方法的内部。

TimeUnit类及其常见用法(详解timedWait、sleep方法)_第4张图片

      最后调用的一个参数的wait方法的声明为public final native void wait(long timeout) throws InterruptedException。

      可以看到,这是一个非Java实现的native方法,这个方法具体实现如何,这里就不继续讨论了。我们只需要知道,这个方法会以毫秒为单位,暂停指定的时间,例如本例当中的5000毫秒。

      注意,毫秒,毫秒,毫秒!本例中,我传参为5秒,结果是当前对象等待5000毫秒,自然而然,毫无疑问。

      可是,如果,我最开始传到的是5微妙,远小于1毫秒,那怎么办呢?可能,你会想,那没有1毫秒,那就等待0.005毫秒不就好了吗?想法确实挺好的,可是,人家wait(long timeout)方法明确告诉你了传递的是一个long型参数。 这......

      到这时候,如果你能联想到上面timedWait方法里所作的毫秒纳秒处理,以及wait方法里最后一个if判断,那就基本知道该如何做了。

      假如参数为5微妙,在timedWait方法里,先作毫秒处理,发现不足1毫秒,于是ms等于0。然后再作纳秒处理,通过计算,将5微妙转换成5000纳秒。然后再调用Object的wait方法,将0毫秒和5000纳秒传递进去。在wait里面,最后一个if语句,判断5000>0,于是timeout++,也就是说,本来传进来的是0毫秒,现在变成了1毫秒。最后,把这个代表1毫秒的1传递进wait(long timeout)方法。

      原来,不足1毫秒的,是当作1毫秒来对待。

      我想,通过这个例子,我们不仅明白了timedWait方法的处理逻辑,也明白了Object类的wait(long timeout, int nanos)方法,以及native方法wait(long timeout)。

      明白了这个,我们再回到开始的问题,为什么说“timeWait是将超时参数转换为 Object.wait 方法所需格式的便捷方法”?因为两个参数的wait(long timeout, int nanos)方法只能接收长整型毫秒和长整型的纳秒参数,一个参数的wait(long timeout)方法只能接收长整型毫秒参数。如果我们想等待的时间不是以毫秒为单位,比方说2小时,3分钟,8秒,5纳秒等等,就需要我们手动去算,算一下2小时等于多少毫秒,3分钟等于多少毫秒,5纳秒怎么修正,等等。这太麻烦了,代码书极其不方便。

      再一个就是可读性,比方说wait(20000000),乍一看,你知道它到底等待多长时间吗?除非你是心算天才。当然,我们可以作些优化,比方说写成wait(20*1000*1000)这种形式,可读性就好一点,不过,仍然会很别扭,而且需要将原始时间通过计算得出多少毫秒,再去拼凑这种形式,依然麻烦。

      但是,timedWait就不一样了,可以接受天、小时、分钟、秒、毫秒、微妙、纳秒,它底层自动会把我们的时间转换成毫秒(也就是Object.wait 方法所需的格式)。这就很方便了。

      还有些需要注意的就是异常问题,通过源码分析,我们可以很明显的看出,在调用timedWait方法的时候,主要会抛出这几个异常:

      全程都可能会抛出的InterruptedException,还有调用wait(long timeout, int nanos)方法可能会抛出的IllegalArgumentException。

6、public void sleep(long timeout)

      使线程休眠指定时间。

      使用此单元执行 Thread.sleep。这是将时间参数转换为 Thread.sleep 方法所需格式的便捷方法。

      参数:timeout,休眠的最短时间。如果小于等于 0,则根本不会休眠。

      抛出:InterruptedException,如果休眠时中断。

      用法很简单。如:

       TimeUnit.MINUTES.sleep(3);    // 休眠3分钟 

       TimeUnit.SECONDS.sleep(6);    // 休眠六秒钟

       在这里我们看到了和timedWait方法描述很相似的一句话“是将时间参数转换为 Thread.sleep 方法所需格式的便捷方法”。

       为什么呢?其实和上面(5)介绍的timedWait方法一样,(如果没有看上面的timedWait源码分析,建议看一下,因为逻辑跟sleep基本完全一样),因为Thread的sleep(long timeout)方法只能接受长整型参数(时间单位是毫秒),Thread.sleep(ms, ns)只能接收长整型毫秒和长整型的纳秒参数。书写起来何其麻烦,并且可读性极其不好。

       而TimeUnit的sleep就不一样了,参数可以代表天、时、分、秒等等,不仅写起来简单,可读性也比原生Thread类里的的sleep方法要好得多。

      这里,再给大家展示一下sleep方法的源码执行流程吧,具体参数处理就不详细说明了,如果有疑问,同样地看(5)对timedWait方法的介绍,一模一样。

TimeUnit类及其常见用法(详解timedWait、sleep方法)_第5张图片

TimeUnit类及其常见用法(详解timedWait、sleep方法)_第6张图片

TimeUnit类及其常见用法(详解timedWait、sleep方法)_第7张图片

      流程,和处理逻辑几乎与timedWait一模一样。

7、public void timedJoin(Thread thread, long timeout)

       使当前线程等待指定时间。

       使用此时间单元执行计时的 Thread.join。这是将时间参数转换为 Thread.join 方法所需格式的便捷方法。

       参数:

    thread - 要等待的线程

    timeout - 要等待的最长时间。如果小于等于 0,则根本不会等待。

       抛出:

    InterruptedException - 如果等待时中断。

   如果明白了timedWait和sleep方法,timedJoin是一样的。这里就不再详细介绍了。

保护原创,人人有责。

你可能感兴趣的:(Java,SE,TimeUnit)