1.概述

转载:【java】Thread.Sleep 与 Thread.onSpinWait

2.Thread.Sleep

一般情况下,我们让线程等待一段时间都是使用Thread.sleep()命令。比如下面这个demo示例:

@Test
public void test9() throws InterruptedException {new Thread(() -> {System.out.println(Thread.currentThread().getName() + " 开始执行");try {while (!b) {TimeUnit.SECONDS.sleep(1);// 其内部也是调用的Thread.sleep实现的}} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " 执行完毕");}).start();TimeUnit.SECONDS.sleep(2);b = true;TimeUnit.SECONDS.sleep(2);System.out.println(Thread.currentThread().getName() + " 执行完毕");
}

运行结果:

Thread-0 开始执行
Thread-0 执行完毕
main 执行完毕

如果我们想要停顿的时间足够短,取一个极端情况,等待时间为0millis: Thread.Sleep(0),那么每次都会停顿0毫秒,然后返回。

但是Thread.Sleep(0)让线程挂起0millis的意义在哪里?意义在于这次调用Thread.Sleep(0)的当前线程暂时放弃cpu,让出CPU使用权,释放当前线程所剩余的时间片(如果有剩余的话),可以让操作系统切换其他线程来执行,就相当于一个让位动作。

然后由于CPU大多是抢占式的,这里让出了CPU,然后停顿0millis,继续去抢占CPU,这个时候不一定会抢得到哦,CPU可能会被其他线程抢走。

在线程没退出之前,线程有三个状态,就绪态,运行态,等待态。sleep(n)之所以在n秒内不会参与CPU竞争,是因为,当线程调用sleep(n)的时候,线程是由运行态转入等待态,线程被放入等待队列中,等待定时器n秒后的中断事件,当到达n秒计时后,线程才重新由等待态转入就绪态,被放入就绪队列中,等待队列中的线程是不参与cpu竞争的,只有就绪队列中的线程才会参与cpu竞争,所谓的cpu调度,就是根据一定的算法(优先级,FIFO等。。。),从就绪队列中选择一个线程来分配cpu时间。

而sleep(0)之所以马上回去参与cpu竞争,是因为调用sleep(0)后,因为0的原因,线程直接回到就绪队列,而非进入等待队列,只要进入就绪队列,那么它就参与cpu竞争。

综上所述,如果想要在循环中让线程等待一段时间,但是又想等待的时间足够短,那么就可以使用Thread.Sleep(0)。这样比直接空循环会节省一些CPU资源。但是每次线程都要挂起转移到等待队列,这也会消耗不少时间,因此即使设置的是等待0,他还是会有一点点的等待时间。

3.Thread.onSpinWait

onSpinWait方法默认是空实现。它被@HotSpotIntrinsicCandidate修饰。JDK的源码中,被@HotSpotIntrinsicCandidate标注的方法,在HotSpot中都有一套高效的实现,该高效实现基于CPU指令,运行时,HotSpot维护的高效实现会替代JDK的源码实现,从而获得更高的效率。
具体HotSpot做了哪些优化,后续再研究吧。或者哪位大神知道可以分享一下。

@HotSpotIntrinsicCandidate
public static void onSpinWait() {}

测试类

int a = 1000000,b=1000000,c=1000000;@Test
public void test10() throws InterruptedException {new Thread(() -> {long begin = System.currentTimeMillis();System.out.println(Thread.currentThread().getName() + " 开始执行");while (a-->0) {try {Thread.sleep(0);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + " 执行完毕  " + (System.currentTimeMillis() - begin));}, "a").start();new Thread(() -> {long begin = System.currentTimeMillis();System.out.println(Thread.currentThread().getName() + " 开始执行");while (b-->0) {Thread.onSpinWait();}System.out.println(Thread.currentThread().getName() + " 执行完毕 " + (System.currentTimeMillis() - begin));}, "b").start();new Thread(() -> {long begin = System.currentTimeMillis();System.out.println(Thread.currentThread().getName() + " 开始执行");while (c-->0) {}System.out.println(Thread.currentThread().getName() + " 执行完毕 " + (System.currentTimeMillis() - begin));}, "c").start();Thread.sleep(1000000000);
}

输出:

a 开始执行
b 开始执行
c 开始执行
c 执行完毕 21
b 执行完毕 60
a 执行完毕  500

上述程序,循环等待100W次。使用Thread.onSpinWait();比Thread.sleep(0);性能要好。
但是c线程空循环耗时最少。不过区别在于空循环不会使用到虚拟机的优化。
官方文档提到:

The code above would remain correct even if the {@code onSpinWait} method was not called at all. However on some architectures the Java Virtual Machine may issue the processor instructions to address such code patterns in a more beneficial way.

简单翻译一下就是:即使不调用Thread.onSpinWait();程序也可以正常运行,但是在一些架构中,HotSpot虚拟机会对其进行优化。

5.总结

使用Thread.onSpinWait();比Thread.sleep(0);性能要好。
Thread.onSpinWait(); 到底比空循环好在哪里,还没有搞清楚。

JDk源码中应用也比较少:Phaser, StampedLock,SynchronousQueue

【java】Thread.Sleep 与 Thread.onSpinWait相关推荐

  1. Java多线程3:Thread中的静态方法

    Thread类中的静态方法 Thread类中的静态方法表示操作的线程是"正在执行静态方法所在的代码块的线程".为什么Thread类中要有静态方法,这样就能对CPU当前正在运行的线程 ...

  2. Java并发编程:Thread类的使用

    为什么80%的码农都做不了架构师?>>>    Java并发编程:Thread类的使用 在前面2篇文章分别讲到了线程和进程的由来.以及如何在Java中怎么创建线程和进程.今天我们来学 ...

  3. java线程学习-Thread.currentTread().getName()和this.getName()的区别

    很久没有写java程序了,由于为了改变目前的状况,打算花两天时间学习一下java的线程开发和高并发. 线程开发使用thread类,或者runnable接口,而且thread类也是实现了runnable ...

  4. Java多线程2:Thread中的实例方法

    Thread类中的方法调用方式: 学习Thread类中的方法是学习多线程的第一步.在学习多线程之前特别提出一点,调用Thread中的方法的时候,在线程类中,有两种方式,一定要理解这两种方式的区别: 1 ...

  5. (一)java多线程之Thread

    目录 Thread类 创建一个线程 java创建线程有两种方式 实现 区别 让线程"睡"一会 停止线程 线程的属性 Thread.join() Thread.yield 线程的异常 ...

  6. Java中如何使用Thread和Runnable创建的线程

    前言 我们都知道通过继承Thread和实现Runnable接口都能创建线程,那么他们有什么区别呢? 继承Thread创建线程 通过extends关键字继承Thread然后覆盖其run方法就可以实现一个 ...

  7. Java中therad_java中Thread的深入了解(一)

    线程 线程是进程中的一个单一顺序的执行单元,也被称为轻量进程(lightweight process).线程有自己的必须资源,同时与其他线程进程共享全部资源.同一个进程中,线程是并发执行的.由于线程的 ...

  8. C#,JAVA各版本之Thread.join()详解

    join方法的功能就是使异步执行的线程变成同步执行.也就是说,当调用线程实例的start方法后,这个方法会立即返回,如果在调用start方法后后需要使用一个由这个线程计算得到的值,就必须使用join方 ...

  9. 再谈 Java中Runnable和Thread的区别

    在面试中老有面试官问这个问题,个人觉得这个问题问的没有技术,一个死记硬背就能回答的很好.但是更深的回答是什么了,那就是直接回答源码吧. thread类实现了runnable 接口 ,Runnable就 ...

最新文章

  1. 数据库和MySQL相关面试题目
  2. [jQuery] jQuery中如何将数组转化为json字符串,然后再转化回来?
  3. idea swagger生成接口文档_Spring Boot(九)Swagger2自动生成接口文档和Mock模拟数据...
  4. “我创业成功的十大秘诀”
  5. 网络营销教程—SEO 第五章 单面页最佳优化
  6. 值得收藏:一份非常完整的 MySQL 规范指南
  7. 甲醛|甲醛介绍|甲醛如何清理
  8. 多线程编程(16) - 多线程同步之 WaitableTimer (等待定时器对象)[续二]
  9. oracle into关键字,oracle - INTO保留关键字不能用作标识符 - 堆栈内存溢出
  10. C++入门项目必练100题
  11. matlab剪切板中内容清除,清除剪贴板的内容
  12. PHP的数据类型主要有三大类八小类。
  13. 51单片机ADDA数模转换
  14. 选购笔记本要看清液晶屏幕及主流技术
  15. 转:HiRes高采样率的必要性
  16. SassPassIass
  17. 树莓派实验室python人脸识别_在树莓派上实现人脸识别
  18. 起底飞书:在产品背后,看见现代管理哲学
  19. [NLP]高级词向量表达之ELMo详解
  20. 智慧企业的基础——知识中台

热门文章

  1. 苹果正在研发iPhone检测抑郁症功能
  2. 全国唯一高校!武汉大学首开鸿蒙系统课程
  3. 华谊兄弟:实际控制人王忠军、王忠磊合计减持1.44%公司股份
  4. 三星关闭在华最后一家电脑厂 约850名员工受影响
  5. 财务自由的味道!台积电股价连创新高,经营团队15人持股价值过亿
  6. 笑喷!小区封闭男子将头伸出围栏外理发:又好笑又心酸
  7. 除了速度与激情 领克01带给我另一种有关生活方式的想象
  8. 对话罗伟:5G背景下 物联网领域最大挑战在于商业模式
  9. 12699元的iPhone 11 Pro Max物料成本却不足3500元,苹果赚疯了?
  10. 百度搜索遭遇“假德邦” 宣判结果来了...