【java 多线程】java线程调度之线程睡眠Thread.sleep方法

一、Thread.sleep方法

Thread.sleep(long millis)源码:

    /**
     * Causes the currently executing thread to sleep (temporarily cease
     * execution) for the specified number of milliseconds, subject to
     * the precision and accuracy of system timers and schedulers. The thread
     * does not lose ownership of any monitors.
     *
     * @param  millis
     *         the length of time to sleep in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public static native void sleep(long millis) throws InterruptedException;

注意:
Thread 中的方法public static void sleep(long millis, int nanos)从参数来看,线程睡眠设定的时间为millis + nanos.但实际上并非如此.
来看一下源码:

    /**
     * Causes the currently executing thread to sleep (temporarily cease
     * execution) for the specified number of milliseconds plus the specified
     * number of nanoseconds, subject to the precision and accuracy of system
     * timers and schedulers. The thread does not lose ownership of any
     * monitors.
     *
     * @param  millis
     *         the length of time to sleep in milliseconds
     *
     * @param  nanos
     *         {@code 0-999999} additional nanoseconds to sleep
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative, or the value of
     *          {@code nanos} is not in the range {@code 0-999999}
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public static void sleep(long millis, int nanos)
    throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        sleep(millis);
    }

注意这两处代码

 if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
       millis++;
 }
 sleep(millis);

方法对传入的nanos进行了判断
1.也就是当nanos大于等于500微秒时,millis就加1.当nanos小于500微秒时,不改变millis的值.
2.当millis的值为0时,只要nanos不为0,就将millis设置为1.

因此,从代码上来看,睡眠的最小单位仍为millis.最小的睡眠时间为1millis.

由上面的源码可以看出Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)都是静态方法,线程的sleep方法作用是强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。
当线程睡眠时,它睡在某个地方,在苏醒之前不会返回到可运行状态。
当睡眠时间到期,则返回到可运行状态。

线程睡眠的原因:线程执行太快,或者需要强制进入下一轮,因为Java规范不保证合理的轮换。

睡眠的实现:调用静态方法。

    try {  
        Thread.sleep(1000);  
    } catch (InterruptedException e) {  
        e.printStackTrace();   
    }  

睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠。

package day004;

/**
*
* 项目名称:JavaThread
* 类名称:TestSleep1
* 类描述:
* 创建人:liuc
* 创建时间:2018年3月23日 上午9:20:04
* 修改人:liuc
* 修改时间:2018年3月23日 上午9:20:04
* 修改备注:
* @version
*
*/
public class TestSleep1 extends Thread {
    /**
     * @Title:  TestSleep1   
     * @Description:    TODO(这里用一句话描述这个方法的作用)   
     * @param:  @param string  
     * @throws   
    */
    public TestSleep1(String string) {
        super(string);
    }

    /**
    * (non-Javadoc)
    * @see java.lang.Thread#run()
    */
    public void run() {
        super.run();
        for (int i = 0; i < 10; i++) {
            System.out.println("I am " + getName());
            try {
                sleep(2000);// 暂停,每两秒输出一次
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        TestSleep1 t1 = new TestSleep1("TestSleep1");
        t1.start();
        MyThread1 t2 = new MyThread1("MyThread1");
        t2.start();
    }
}
=============================================================================
package day004;


/**
*
* 项目名称:JavaThread
* 类名称:MyThread1
* 类描述:
* 创建人:liuc
* 创建时间:2018年3月23日 上午9:20:38
* 修改人:liuc
* 修改时间:2018年3月23日 上午9:20:38
* 修改备注:
* @version
*
*/
public class MyThread1 extends Thread{
    MyThread1(String str){
        super(str);  
    }

    /**
    * (non-Javadoc)
    * @see java.lang.Thread#run()
    */
    public void run() {
        super.run();
        for (int i = 0; i < 10; i++) {
             System.out.println("I am "+getName());
             try {
                sleep(1000);//暂停,每一秒输出一次
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

输出结果:

I am TestSleep1
I am MyThread1
I am MyThread1
I am TestSleep1
I am MyThread1
I am MyThread1
I am TestSleep1
I am MyThread1
I am MyThread1
I am TestSleep1
I am MyThread1
I am MyThread1
I am TestSleep1
I am MyThread1
I am MyThread1
I am TestSleep1
I am TestSleep1
I am TestSleep1
I am TestSleep1
I am TestSleep1

注意:
1、线程睡眠是帮助所有线程获得运行机会的最好方法。
2、线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。
3、sleep()是静态方法,只能控制当前正在运行的线程。

二、优先使用TimeUnit类中的sleep()

TimeUnit是什么?

TimeUnit是java.util.concurrent包下面的一个类,TimeUnit提供了可读性更好的线程暂停操作,通常用来替换Thread.sleep(),在很长一段时间里Thread的sleep()方法作为暂停线程的标准方式,几乎所有Java程序员都熟悉它,事实上sleep方法本身也很常用而且出现在很多面试中。如果你已经使用过Thread.sleep(),当然我确信你这样做过,那么你一定熟知它是一个静态方法,暂停线程时它不会释放锁,该方法会抛出InterrupttedException异常(如果有线程中断了当前线程)。但是我们很多人并没有注意的一个潜在的问题就是它的可读性。Thread.sleep()是一个重载方法,可以接收长整型毫秒和长整型的纳秒参数,这样对程序员造成的一个问题就是很难知道到底当前线程是睡眠了多少秒、分、小时或者天。看看下面这个Thread.sleep()方法:

Thread.sleep(2400000

粗略一看,你能计算出当前线程是等待多长时间吗?可能有些人可以,但是对于大多数程序员来说这种写法的可读性还是很差的,你需要把毫秒转换成秒和分,让我们来看看另外一个例子,这个例子比前面那个例子可读性稍微好一点:

Thread.sleep(4*60*1000);

这比前面那个例子已经好多了,但是仍然不是最好的,你注意到睡眠时间用毫秒,不容易猜出当前线程将等待4分钟。TimeUnit类解决了这个问题,通过指定DAYS、HOURS、MINUTES,SECONDS、MILLISECONDS和NANOSECONDS。java.utils.concurrent .TimeUnit 是Java枚举应用场景中最好的例子之一,所有TimeUnit都是枚举实例,让我们来看看线程睡眠4分钟用TimeUnit是如何使用的。

TimeUnit.MINUTES.sleep(4);  // sleeping for 4 minutes

类似你可以采用秒、分、小时级别来暂停当前线程。你可以看到这比Thread的sleep方法的可读的好多了。记住TimeUnit.sleep()内部调用的Thread.sleep()也会抛出InterruptException。你也可以查看JDK源代码去验证一下。下面是一个简单例子,它展示如果使用TimeUnit.sleep()方法。

package day004;

import java.util.concurrent.TimeUnit;

/**
*
* 项目名称:JavaThread
* 类名称:TestTimeUnitSleep
* 类描述:
* 创建人:liuc
* 创建时间:2018年3月23日 上午11:14:42
* 修改人:liuc
* 修改时间:2018年3月23日 上午11:14:42
* 修改备注:
* @version
*
*/
public class TestTimeUnitSleep {
    public static void main(String args[]) throws InterruptedException { 
        System.out.println("Sleeping for 4 minutes using Thread.sleep()");
        Thread.sleep(4 * 60 * 1000);
        System.out.println("Sleeping for 4 minutes using TimeUnit sleep()");
        TimeUnit.SECONDS.sleep(4);
        TimeUnit.MINUTES.sleep(4);
        TimeUnit.HOURS.sleep(1);
        TimeUnit.DAYS.sleep(1);
    }
}

除了sleep的功能外,TimeUnit还提供了便捷方法用于把时间转换成不同单位,例如,如果你想把秒转换成毫秒,你可以使用下面代码:

TimeUnit.SECONDS.toMillis(44)

它将返回44,000

TimeUnit VS Thread.sleep()
目前我们讨论使用TimeUnit的好处是提高了可读性,但是有时候觉得其他方法更好,因为Thread.sleep()伴随java很早就出现了,几乎所有程序员都知道Thread.sleep(),都知道是将当前线程暂停,而对TimeUnit并不太熟悉。两个原因:一是对比起Thread.sleep(),TimeUnit不是很常用,第二是在它不在Thread类中,就像wait和notify同样不是在Thread中,反正这些需要一段时间才能被采用,并成为一个标准的方式。

总结来说在你想用Thread.sleep()方法的地方你最好使用TimeUnit.sleep()方法来代替。它不仅可以提高代码的可读性而且能更加熟悉java.util.concurrent包,因为TimeUnit在并发编程中也是一个关键API。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值