java getdelay_java中DelayQueue的一个使用陷阱分析

最近工作中有接触到DelayQueue,网上搜索资料的时候发现一篇文章谈到DelayQueue的坑。点击打开链接

文中已经总结了遇到坑的地方,还有解决方案。不过我第一眼看一下没弄明白为什么,所以翻了翻源码深究了一下,下面把这个坑的原因以及原理分析一下。

首先是DelayQueue的take()方法:

1 public E take() throwsInterruptedException {2 final ReentrantLock lock = this.lock;3 lock.lockInterruptibly();4 try{5 for(;;) {6 E first =q.peek();7 if (first == null)8 available.await();9 else{10 long delay = first.getDelay(NANOSECONDS); //1

11 if (delay <= 0)12 returnq.poll();13 first = null; //don't retain ref while waiting

14 if (leader != null)15 available.await();16 else{17 Thread thisThread =Thread.currentThread();18 leader =thisThread;19 try{20 available.awaitNanos(delay); //2

21 } finally{22 if (leader ==thisThread)23 leader = null;24 }25 }26 }27 }28 } finally{29 if (leader == null && q.peek() != null)30 available.signal();31 lock.unlock();32 }33 }

首先看到注释2,这是一个带时间的await方法,时间单位是纳秒,传入的参数delay是从注释1通过调用first对象的getDelay方法获取的。first对象是E类型的,E是一个实现了Delayed接口的泛型。

这里看看接口Delayed的源码:

1 public interface Delayed extends Comparable{2

3 /**

4 * Returns the remaining delay associated with this object, in the5 * given time unit.6 *7 *@paramunit the time unit8 *@returnthe remaining delay; zero or negative values indicate9 * that the delay has already elapsed10 */

11 longgetDelay(TimeUnit unit);12 }

就只有一个getDelay(TimeUnit)方法,它返回的指定的TimeUnit的时间长度。显然,具体的实现类要实现该方法才行。

那么来看一下具体的getDelay(TimeUnit)方法的实现吧,我看了几篇文章,基本上大同小异,都是如下这般实现的:

1 public longgetDelay(TimeUnit unit) {2 return unit.convert(this.expire -System.currentTimeMillis() , TimeUnit.MILLISECONDS);3 }

原博主很贴心的提醒了,这个地方convert方法的第二个参数,应该要使用TimeUnit.MILLISECONDS而不是TimeUnit.NANOSECONDS(虽然不管使用什么时间单位都不会导致程序出现错误的结果,但是用错了时间单位的话,CPU可就遭殃了)。那么为什么会一定要强调要使用MILLISECONDS这个单位呢?

继续看看convert方法的源码吧,在TimeUnit枚举类中,定义了若干时间单位,他们有各自的convert方法的实现,先来看看TimeUnit.NANOSECONDS的:

1 NANOSECONDS {2 public long toNanos(long d) { returnd; }3 public long toMicros(long d) { return d/(C1/C0); }4 public long toMillis(long d) { return d/(C2/C0); }5 public long toSeconds(long d) { return d/(C3/C0); }6 public long toMinutes(long d) { return d/(C4/C0); }7 public long toHours(long d) { return d/(C5/C0); }8 public long toDays(long d) { return d/(C6/C0); }9 public long convert(long d, TimeUnit u) { returnu.toNanos(d); }10 int excessNanos(long d, long m) { return (int)(d - (m*C2)); }11 },

可以看到,convert方法又直接调用了TimeUnit.toNanos方法,直接就把第一个参数d当做一个纳秒的时间长度给返回了。

同理看看TimeUnit.MILLISECONDS定义的方法:

1 MILLISECONDS {2 public long toNanos(long d) { return x(d, C2/C0, MAX/(C2/C0)); } //static final long C0 = 1L; static final long C1 = C0 * 1000L;static final long C2 = C1 * 1000L;

3 public long toMicros(long d) { return x(d, C2/C1, MAX/(C2/C1)); }4 public long toMillis(long d) { returnd; }5 public long toSeconds(long d) { return d/(C3/C2); }6 public long toMinutes(long d) { return d/(C4/C2); }7 public long toHours(long d) { return d/(C5/C2); }8 public long toDays(long d) { return d/(C6/C2); }9 public long convert(long d, TimeUnit u) { returnu.toMillis(d); }10 int excessNanos(long d, long m) { return 0; }11 },

回到我们的实际使用场景,take方法中long delay = first.getDelay(NANOSECONDS);  ->  NANOSECONDS.convert(long d, TimeUnit u)  ->  u.toNanos(d)。如果我们在getDelay方法实现中,convert方法第二个参数传入的是NANOSECONDS,那么就直接返回d;如果convert方法第二个参数传入的是MILLISECONDS,那么返回就是MILLISECONDS.toNanos(d),得到的结果就是1000*1000*d。

可以发现,convert方法的第二个参数TimeUnit,实际上是跟着第一个参数d的时间单位走的。如果实现时候直接使用time - System.currentTimeMillis()作为第一个参数,实际上它的时间单位确实应该是MILLISECONDS,那么如果第二个参数传错了为NANOSECONDS,那就导致take方法中的awaitNanos方法等待时间缩短了1000*1000倍,这样带来的cpu空转压力是巨大的。

分析了这么多,其实看看jdk中TimeUnit类对convert方法的注释,很容易就理解了:

/**

* Converts the given time duration in the given unit to this unit.

* Conversions from finer to coarser granularities truncate, so

* lose precision. For example, converting {@code 999} milliseconds

* to seconds results in {@code 0}. Conversions from coarser to

* finer granularities with arguments that would numerically

* overflow saturate to {@code Long.MIN_VALUE} if negative or

* {@code Long.MAX_VALUE} if positive.

*

*

For example, to convert 10 minutes to milliseconds, use:

* {@code TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES)}

*

* @param sourceDuration the time duration in the given {@code sourceUnit}

* @param sourceUnit the unit of the {@code sourceDuration} argument

* @return the converted duration in this unit,

* or {@code Long.MIN_VALUE} if conversion would negatively

* overflow, or {@code Long.MAX_VALUE} if it would positively overflow.

*/

public long convert(long sourceDuration, TimeUnit sourceUnit) {

throw new AbstractMethodError();

}

这里很明确的指出了,convert方法的第二个参数sourceUnit(@param sourceUnit the unit of the {@code sourceDuration} argument)应该是第一个参数sourceDuration的时间单位。会产生原链接中提到的那样的错误使用,应该就是理解错了这个convert方法参数的含义,以为第二个参数的时间单位是要转换到的时间单位。

不过这个陷阱确实有点绕,在getDelay(TimeUnit unit)方法里面,调用unit.convert(long sourceDuration, TimeUnit sourceUnit)方法,一下出来了两个TimeUnit变量,不仔细一点的话真是容易被坑啊。当然,要是自身的getDelay方法实现不用unit.convert方法或许就避免了该问题了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/562005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML+CSS+JS实现美女照片3D立方体旋转特效

&#x1f345; 作者主页&#xff1a;Java李杨勇 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java李阳勇 公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我&#xff0c;都给你】 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

JAVA回调函数的例子_javascript : 回调函数例子

数字时钟 digital_clock.htmlDigital Clock// Define a function to display the current timefunction displayTime() {var elt document.getElementById("clock"); // Find element with id"clock"var now new Date(); // Get current timeelt.innerHT…

HTML+CSS+JS实现 ❤️canvas酷炫表白爱心动画❤️

&#x1f345; 作者主页&#xff1a;Java李杨勇 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java李杨勇公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我&#xff0c;都给你】 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f…

HTML+CSS+JS实现 ❤️酷炫HUD科幻数据屏幕动画界面❤️

&#x1f345; 作者主页&#xff1a;Java李杨勇 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java李杨勇公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我&#xff0c;都给你】 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f…

java 条件 等待_java – 如何唤醒等待相同条件的所有线程?

我有以下情况.几个线程正在等待相同的条件.当得到通知时,所有应该停止等待,更改标志并返回对象&#xff1a;public Object getObject(){lock.lock();try {while (check)){condition.await();}return returnObjectAndSetCheckToFalse();} finally {lock.unlock();}}但是这段代码…

基于Springboot+Mybatis实现个人理财系统

&#x1f345; 作者主页&#xff1a;Java李杨勇 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java李杨勇公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我&#xff0c;都给你】 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f…

java 递归遍历对象所有属性_Java学习之Xml系列二:xml按条件查询、xml递归遍历所有元素和属性...

xml中加入了几条&#xff0c;为了方便查询时作为示例。话不多说见代码注释&#xff1a;DTD文件&#xff1a;SwordTypeDefinition.dtdXML文件&#xff1a;SwordLib.xmlSwordLibrary SYSTEM "SwordTypeDefinition.dtd">欢欣之刃100010夜叉205030魔棒2000java代码&am…

HTML+CSS+JS实现 ❤️酷炫3D瀑布流动画特效❤️

&#x1f345; 作者主页&#xff1a;Java李杨勇 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java李杨勇公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我&#xff0c;都给你】 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f…

java 取dataset_Java LineDataSet.getYVals方法代码示例

import com.github.mikephil.charting.data.LineDataSet; //导入方法依赖的package包/类protected void drawCircles(Canvas c) {mRenderPaint.setStyle(Paint.Style.FILL);float phaseX mAnimator.getPhaseX();float phaseY mAnimator.getPhaseY();List dataSets mChart.ge…

HTML+CSS+JS实现 ❤️canvas圆形水波进度条动画特效❤️

&#x1f345; 作者主页&#xff1a;Java李杨勇 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java李杨勇公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我&#xff0c;都给你】 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f…

HTML+CSS+JS实现 ❤️圆圈波纹动画特效❤️

&#x1f345; 作者主页&#xff1a;Java李杨勇 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java李杨勇公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我&#xff0c;都给你】 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f…

java 二叉树迭代器_C,为二叉树实现自定义迭代器(长)

请你好 - 这是我的第一个问题 . P基本上作为夏季项目&#xff0c;我一直在浏览wikipedia page上的数据结构列表并尝试实现它们 . 我上学期参加了一门C课程并发现它非常有趣&#xff0c;作为我实施二项式堆的最后一个项目 - 这也非常有趣 . 也许我很讨厌&#xff0c;但我喜欢数…

java防止编码重复_java – 如何避免许多小类的代码重复?

我有不同的课程,分别是英语,西班牙语,法语等&#xff1a;Class English{String name "English";String alias "ENG";}Class French{String name "French";String alias "Fre";}与其他语言类似.还有一个叫做语言的课&#xff1a;Cl…

HTML+CSS+JS实现 ❤️6种transform图片悬停动态效果❤️

&#x1f345; 作者主页&#xff1a;Java李杨勇 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java李杨勇公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我&#xff0c;都给你】 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f…

ldap协议 java_java如何调用ldap协议【LdapContext】

背景&#xff1a;做了个系统需要用集团的用户id登录。而集团用户系统是用ldap做的。关键知识点1. 设置连接ctx new InitialLdapContext(env, connCtls);2.设置url和查询的子路径env.put(Context.PROVIDER_URL, URL);// LDAP serverenv.put(Context.SECURITY_PRINCIPAL, SEARCH…

HTML+CSS+JS实现 ❤️经典霓虹灯英文字母特效❤️

&#x1f345; 作者主页&#xff1a;Java李杨勇 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java李杨勇公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我&#xff0c;都给你】 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f…

操作系统分区分配java算法_合肥工业大学操作系统课程设计 动态分区分配存储管理java版.doc...

合肥工业大学操作系统课程设计 动态分区分配存储管理java版.doc 1课程设计动态分区分配存储管理设计题目学号专业班级学生姓名号指导教师22010年12月合肥工业大学课程设计任务书设计题目动态分区分配存储管理成绩主要内容动态分区分配存储管理建立描述内存分配状况的数据结构&a…

HTML+CSS+JS实现 ❤️感谢关注3D文字动画特效❤️

&#x1f345; 作者主页&#xff1a;Java李杨勇 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java李杨勇公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我&#xff0c;都给你】 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f…

java中字符串后加Box_字符串未显示在JavaFX中ComboBox的Tableview上

我一直在尝试在Java中显示我从组合框到表视图的选择 . 我花了好几个小时都在寻找解决方案&#xff0c;但没有用 . 我没有任何错误&#xff0c;但我似乎无法在TableView上显示一个字符串 . 我想请求帮助 .MainView.java有组合框和单击按钮的位置&#xff0c;它应该将组合框中的字…

HTML+CSS+JS实现 ❤️基于Javascript简单计算器特效❤️

&#x1f345; 作者主页&#xff1a;Java李杨勇 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java李杨勇公号作者✌ 简历模板、学习资料、面试题库、技术互助【关注我&#xff0c;都给你】 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f…