日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時間:2025/3/12 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java getdelay_java中DelayQueue的一个使用陷阱分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

最近工作中有接觸到DelayQueue,網(wǎng)上搜索資料的時候發(fā)現(xiàn)一篇文章談到DelayQueue的坑。點擊打開鏈接

文中已經(jīng)總結(jié)了遇到坑的地方,還有解決方案。不過我第一眼看一下沒弄明白為什么,所以翻了翻源碼深究了一下,下面把這個坑的原因以及原理分析一下。

首先是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方法,時間單位是納秒,傳入的參數(shù)delay是從注釋1通過調(diào)用first對象的getDelay方法獲取的。first對象是E類型的,E是一個實現(xiàn)了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的時間長度。顯然,具體的實現(xiàn)類要實現(xiàn)該方法才行。

那么來看一下具體的getDelay(TimeUnit)方法的實現(xiàn)吧,我看了幾篇文章,基本上大同小異,都是如下這般實現(xiàn)的:

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

原博主很貼心的提醒了,這個地方convert方法的第二個參數(shù),應(yīng)該要使用TimeUnit.MILLISECONDS而不是TimeUnit.NANOSECONDS(雖然不管使用什么時間單位都不會導(dǎo)致程序出現(xiàn)錯誤的結(jié)果,但是用錯了時間單位的話,CPU可就遭殃了)。那么為什么會一定要強調(diào)要使用MILLISECONDS這個單位呢?

繼續(xù)看看convert方法的源碼吧,在TimeUnit枚舉類中,定義了若干時間單位,他們有各自的convert方法的實現(xiàn),先來看看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方法又直接調(diào)用了TimeUnit.toNanos方法,直接就把第一個參數(shù)d當(dāng)做一個納秒的時間長度給返回了。

同理看看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方法中l(wèi)ong delay = first.getDelay(NANOSECONDS); ?-> ?NANOSECONDS.convert(long d, TimeUnit u) ?-> ?u.toNanos(d)。如果我們在getDelay方法實現(xiàn)中,convert方法第二個參數(shù)傳入的是NANOSECONDS,那么就直接返回d;如果convert方法第二個參數(shù)傳入的是MILLISECONDS,那么返回就是MILLISECONDS.toNanos(d),得到的結(jié)果就是1000*1000*d。

可以發(fā)現(xiàn),convert方法的第二個參數(shù)TimeUnit,實際上是跟著第一個參數(shù)d的時間單位走的。如果實現(xiàn)時候直接使用time - System.currentTimeMillis()作為第一個參數(shù),實際上它的時間單位確實應(yīng)該是MILLISECONDS,那么如果第二個參數(shù)傳錯了為NANOSECONDS,那就導(dǎo)致take方法中的awaitNanos方法等待時間縮短了1000*1000倍,這樣帶來的cpu空轉(zhuǎn)壓力是巨大的。

分析了這么多,其實看看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方法的第二個參數(shù)sourceUnit(@param sourceUnit the unit of the {@code sourceDuration} argument)應(yīng)該是第一個參數(shù)sourceDuration的時間單位。會產(chǎn)生原鏈接中提到的那樣的錯誤使用,應(yīng)該就是理解錯了這個convert方法參數(shù)的含義,以為第二個參數(shù)的時間單位是要轉(zhuǎn)換到的時間單位。

不過這個陷阱確實有點繞,在getDelay(TimeUnit unit)方法里面,調(diào)用unit.convert(long sourceDuration, TimeUnit sourceUnit)方法,一下出來了兩個TimeUnit變量,不仔細一點的話真是容易被坑啊。當(dāng)然,要是自身的getDelay方法實現(xiàn)不用unit.convert方法或許就避免了該問題了。

總結(jié)

以上是生活随笔為你收集整理的java getdelay_java中DelayQueue的一个使用陷阱分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。