mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8305092: Improve Thread.sleep(millis, nanos) for sub-millisecond granularity
Reviewed-by: dholmes, alanb
This commit is contained in:
parent
891530fbc9
commit
fcb280a48b
11 changed files with 245 additions and 28 deletions
|
@ -1538,6 +1538,12 @@ void PlatformEvent::park() { // AKA "down()"
|
||||||
}
|
}
|
||||||
|
|
||||||
int PlatformEvent::park(jlong millis) {
|
int PlatformEvent::park(jlong millis) {
|
||||||
|
return park_nanos(millis_to_nanos_bounded(millis));
|
||||||
|
}
|
||||||
|
|
||||||
|
int PlatformEvent::park_nanos(jlong nanos) {
|
||||||
|
assert(nanos > 0, "nanos are positive");
|
||||||
|
|
||||||
// Transitions for _event:
|
// Transitions for _event:
|
||||||
// -1 => -1 : illegal
|
// -1 => -1 : illegal
|
||||||
// 1 => 0 : pass - return immediately
|
// 1 => 0 : pass - return immediately
|
||||||
|
@ -1557,7 +1563,7 @@ int PlatformEvent::park(jlong millis) {
|
||||||
|
|
||||||
if (v == 0) { // Do this the hard way by blocking ...
|
if (v == 0) { // Do this the hard way by blocking ...
|
||||||
struct timespec abst;
|
struct timespec abst;
|
||||||
to_abstime(&abst, millis_to_nanos_bounded(millis), false, false);
|
to_abstime(&abst, nanos, false, false);
|
||||||
|
|
||||||
int ret = OS_TIMEOUT;
|
int ret = OS_TIMEOUT;
|
||||||
int status = pthread_mutex_lock(_mutex);
|
int status = pthread_mutex_lock(_mutex);
|
||||||
|
|
|
@ -54,6 +54,7 @@ class PlatformEvent : public CHeapObj<mtSynchronizer> {
|
||||||
PlatformEvent();
|
PlatformEvent();
|
||||||
void park();
|
void park();
|
||||||
int park(jlong millis);
|
int park(jlong millis);
|
||||||
|
int park_nanos(jlong nanos);
|
||||||
void unpark();
|
void unpark();
|
||||||
|
|
||||||
// Use caution with reset() and fired() -- they may require MEMBARs
|
// Use caution with reset() and fired() -- they may require MEMBARs
|
||||||
|
|
|
@ -5249,6 +5249,21 @@ class HighResolutionInterval : public CHeapObj<mtThread> {
|
||||||
// explicit "PARKED" == 01b and "SIGNALED" == 10b bits.
|
// explicit "PARKED" == 01b and "SIGNALED" == 10b bits.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
int PlatformEvent::park_nanos(jlong nanos) {
|
||||||
|
assert(nanos > 0, "nanos are positive");
|
||||||
|
|
||||||
|
// Windows timers are still quite unpredictable to handle sub-millisecond granularity.
|
||||||
|
// Instead of implementing sub-millisecond sleeps, fall back to the usual behavior of
|
||||||
|
// rounding up any excess requested nanos to the full millisecond. This is how
|
||||||
|
// Thread.sleep(millis, nanos) has always behaved with only millisecond granularity.
|
||||||
|
jlong millis = nanos / NANOSECS_PER_MILLISEC;
|
||||||
|
if (nanos > millis * NANOSECS_PER_MILLISEC) {
|
||||||
|
millis++;
|
||||||
|
}
|
||||||
|
assert(millis > 0, "should always be positive");
|
||||||
|
return park(millis);
|
||||||
|
}
|
||||||
|
|
||||||
int PlatformEvent::park(jlong Millis) {
|
int PlatformEvent::park(jlong Millis) {
|
||||||
// Transitions for _Event:
|
// Transitions for _Event:
|
||||||
// -1 => -1 : illegal
|
// -1 => -1 : illegal
|
||||||
|
|
|
@ -48,9 +48,10 @@ class PlatformEvent : public CHeapObj<mtSynchronizer> {
|
||||||
// Exercise caution using reset() and fired() - they may require MEMBARs
|
// Exercise caution using reset() and fired() - they may require MEMBARs
|
||||||
void reset() { _Event = 0 ; }
|
void reset() { _Event = 0 ; }
|
||||||
int fired() { return _Event; }
|
int fired() { return _Event; }
|
||||||
void park () ;
|
void park();
|
||||||
void unpark () ;
|
void unpark();
|
||||||
int park (jlong millis) ;
|
int park(jlong millis);
|
||||||
|
int park_nanos(jlong nanos);
|
||||||
};
|
};
|
||||||
|
|
||||||
class PlatformParker {
|
class PlatformParker {
|
||||||
|
|
|
@ -276,7 +276,7 @@ JNIEXPORT void JNICALL
|
||||||
JVM_Yield(JNIEnv *env, jclass threadClass);
|
JVM_Yield(JNIEnv *env, jclass threadClass);
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
JVM_Sleep(JNIEnv *env, jclass threadClass, jlong millis);
|
JVM_Sleep(JNIEnv *env, jclass threadClass, jlong nanos);
|
||||||
|
|
||||||
JNIEXPORT jobject JNICALL
|
JNIEXPORT jobject JNICALL
|
||||||
JVM_CurrentCarrierThread(JNIEnv *env, jclass threadClass);
|
JVM_CurrentCarrierThread(JNIEnv *env, jclass threadClass);
|
||||||
|
|
|
@ -3044,9 +3044,9 @@ JVM_LEAF(void, JVM_Yield(JNIEnv *env, jclass threadClass))
|
||||||
os::naked_yield();
|
os::naked_yield();
|
||||||
JVM_END
|
JVM_END
|
||||||
|
|
||||||
JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
|
JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong nanos))
|
||||||
if (millis < 0) {
|
if (nanos < 0) {
|
||||||
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
|
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "nanosecond timeout value out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thread->is_interrupted(true) && !HAS_PENDING_EXCEPTION) {
|
if (thread->is_interrupted(true) && !HAS_PENDING_EXCEPTION) {
|
||||||
|
@ -3057,14 +3057,14 @@ JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
|
||||||
// And set new thread state to SLEEPING.
|
// And set new thread state to SLEEPING.
|
||||||
JavaThreadSleepState jtss(thread);
|
JavaThreadSleepState jtss(thread);
|
||||||
|
|
||||||
HOTSPOT_THREAD_SLEEP_BEGIN(millis);
|
HOTSPOT_THREAD_SLEEP_BEGIN(nanos / NANOSECS_PER_MILLISEC);
|
||||||
|
|
||||||
if (millis == 0) {
|
if (nanos == 0) {
|
||||||
os::naked_yield();
|
os::naked_yield();
|
||||||
} else {
|
} else {
|
||||||
ThreadState old_state = thread->osthread()->get_state();
|
ThreadState old_state = thread->osthread()->get_state();
|
||||||
thread->osthread()->set_state(SLEEPING);
|
thread->osthread()->set_state(SLEEPING);
|
||||||
if (!thread->sleep(millis)) { // interrupted
|
if (!thread->sleep_nanos(nanos)) { // interrupted
|
||||||
// An asynchronous exception could have been thrown on
|
// An asynchronous exception could have been thrown on
|
||||||
// us while we were sleeping. We do not overwrite those.
|
// us while we were sleeping. We do not overwrite those.
|
||||||
if (!HAS_PENDING_EXCEPTION) {
|
if (!HAS_PENDING_EXCEPTION) {
|
||||||
|
|
|
@ -1982,11 +1982,24 @@ Klass* JavaThread::security_get_caller_class(int depth) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Internal convenience function for millisecond resolution sleeps.
|
||||||
|
bool JavaThread::sleep(jlong millis) {
|
||||||
|
jlong nanos;
|
||||||
|
if (millis > max_jlong / NANOUNITS_PER_MILLIUNIT) {
|
||||||
|
// Conversion to nanos would overflow, saturate at max
|
||||||
|
nanos = max_jlong;
|
||||||
|
} else {
|
||||||
|
nanos = millis * NANOUNITS_PER_MILLIUNIT;
|
||||||
|
}
|
||||||
|
return sleep_nanos(nanos);
|
||||||
|
}
|
||||||
|
|
||||||
// java.lang.Thread.sleep support
|
// java.lang.Thread.sleep support
|
||||||
// Returns true if sleep time elapsed as expected, and false
|
// Returns true if sleep time elapsed as expected, and false
|
||||||
// if the thread was interrupted.
|
// if the thread was interrupted.
|
||||||
bool JavaThread::sleep(jlong millis) {
|
bool JavaThread::sleep_nanos(jlong nanos) {
|
||||||
assert(this == Thread::current(), "thread consistency check");
|
assert(this == Thread::current(), "thread consistency check");
|
||||||
|
assert(nanos >= 0, "nanos are in range");
|
||||||
|
|
||||||
ParkEvent * const slp = this->_SleepEvent;
|
ParkEvent * const slp = this->_SleepEvent;
|
||||||
// Because there can be races with thread interruption sending an unpark()
|
// Because there can be races with thread interruption sending an unpark()
|
||||||
|
@ -2000,20 +2013,22 @@ bool JavaThread::sleep(jlong millis) {
|
||||||
|
|
||||||
jlong prevtime = os::javaTimeNanos();
|
jlong prevtime = os::javaTimeNanos();
|
||||||
|
|
||||||
|
jlong nanos_remaining = nanos;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// interruption has precedence over timing out
|
// interruption has precedence over timing out
|
||||||
if (this->is_interrupted(true)) {
|
if (this->is_interrupted(true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (millis <= 0) {
|
if (nanos_remaining <= 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ThreadBlockInVM tbivm(this);
|
ThreadBlockInVM tbivm(this);
|
||||||
OSThreadWaitState osts(this->osthread(), false /* not Object.wait() */);
|
OSThreadWaitState osts(this->osthread(), false /* not Object.wait() */);
|
||||||
slp->park(millis);
|
slp->park_nanos(nanos_remaining);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update elapsed time tracking
|
// Update elapsed time tracking
|
||||||
|
@ -2024,7 +2039,7 @@ bool JavaThread::sleep(jlong millis) {
|
||||||
assert(false,
|
assert(false,
|
||||||
"unexpected time moving backwards detected in JavaThread::sleep()");
|
"unexpected time moving backwards detected in JavaThread::sleep()");
|
||||||
} else {
|
} else {
|
||||||
millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;
|
nanos_remaining -= (newtime - prevtime);
|
||||||
}
|
}
|
||||||
prevtime = newtime;
|
prevtime = newtime;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1140,6 +1140,7 @@ private:
|
||||||
ParkEvent * _SleepEvent;
|
ParkEvent * _SleepEvent;
|
||||||
public:
|
public:
|
||||||
bool sleep(jlong millis);
|
bool sleep(jlong millis);
|
||||||
|
bool sleep_nanos(jlong nanos);
|
||||||
|
|
||||||
// java.lang.Thread interruption support
|
// java.lang.Thread interruption support
|
||||||
void interrupt();
|
void interrupt();
|
||||||
|
|
|
@ -506,14 +506,14 @@ public class Thread implements Runnable {
|
||||||
if (currentThread() instanceof VirtualThread vthread) {
|
if (currentThread() instanceof VirtualThread vthread) {
|
||||||
vthread.sleepNanos(nanos);
|
vthread.sleepNanos(nanos);
|
||||||
} else {
|
} else {
|
||||||
sleep0(millis);
|
sleep0(nanos);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
afterSleep(event);
|
afterSleep(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static native void sleep0(long millis) throws InterruptedException;
|
private static native void sleep0(long nanos) throws InterruptedException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Causes the currently executing thread to sleep (temporarily cease
|
* Causes the currently executing thread to sleep (temporarily cease
|
||||||
|
@ -555,11 +555,7 @@ public class Thread implements Runnable {
|
||||||
if (currentThread() instanceof VirtualThread vthread) {
|
if (currentThread() instanceof VirtualThread vthread) {
|
||||||
vthread.sleepNanos(totalNanos);
|
vthread.sleepNanos(totalNanos);
|
||||||
} else {
|
} else {
|
||||||
// millisecond precision
|
sleep0(totalNanos);
|
||||||
if (nanos > 0 && millis < Long.MAX_VALUE) {
|
|
||||||
millis++;
|
|
||||||
}
|
|
||||||
sleep0(millis);
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
afterSleep(event);
|
afterSleep(event);
|
||||||
|
@ -593,12 +589,7 @@ public class Thread implements Runnable {
|
||||||
if (currentThread() instanceof VirtualThread vthread) {
|
if (currentThread() instanceof VirtualThread vthread) {
|
||||||
vthread.sleepNanos(nanos);
|
vthread.sleepNanos(nanos);
|
||||||
} else {
|
} else {
|
||||||
// millisecond precision
|
sleep0(nanos);
|
||||||
long millis = NANOSECONDS.toMillis(nanos);
|
|
||||||
if (nanos > MILLISECONDS.toNanos(millis)) {
|
|
||||||
millis += 1L;
|
|
||||||
}
|
|
||||||
sleep0(millis);
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
afterSleep(event);
|
afterSleep(event);
|
||||||
|
|
119
test/jdk/java/lang/Thread/SleepSanity.java
Normal file
119
test/jdk/java/lang/Thread/SleepSanity.java
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @summary Sanity test Thread.sleep behavior
|
||||||
|
* @run junit SleepSanity
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class SleepSanity {
|
||||||
|
|
||||||
|
static final int[] TRY_MILLIS = new int[] { 0, 1, 10, 100, 1_000 };
|
||||||
|
static final int[] TRY_NANOS = new int[] { 0, 1, 10, 100, 1_000, 10_000, 100_000, 999_999 };
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testMillis() throws Exception {
|
||||||
|
testIAE(() -> Thread.sleep(-1), "timeout value is negative");
|
||||||
|
|
||||||
|
testTimeout(() -> Thread.sleep(10_000), 5_000);
|
||||||
|
testTimeout(() -> Thread.sleep(Integer.MAX_VALUE), 5_000);
|
||||||
|
testTimeout(() -> Thread.sleep(Long.MAX_VALUE), 5_000);
|
||||||
|
|
||||||
|
for (final int millis : TRY_MILLIS) {
|
||||||
|
testTimes(() -> Thread.sleep(millis), millis, 20_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testMillisNanos() throws Exception {
|
||||||
|
testIAE(() -> Thread.sleep(-1), "timeout value is negative");
|
||||||
|
|
||||||
|
testIAE(() -> Thread.sleep(0, -1), "nanosecond timeout value out of range");
|
||||||
|
testIAE(() -> Thread.sleep(0, 1_000_000), "nanosecond timeout value out of range");
|
||||||
|
testIAE(() -> Thread.sleep(0, Integer.MAX_VALUE), "nanosecond timeout value out of range");
|
||||||
|
|
||||||
|
testTimeout(() -> Thread.sleep(10_000, 0), 5_000);
|
||||||
|
testTimeout(() -> Thread.sleep(Integer.MAX_VALUE, 0), 5_000);
|
||||||
|
testTimeout(() -> Thread.sleep(Long.MAX_VALUE, 0), 5_000);
|
||||||
|
|
||||||
|
testTimeout(() -> Thread.sleep(10_000, 999_999), 5_000);
|
||||||
|
testTimeout(() -> Thread.sleep(Integer.MAX_VALUE, 999_999), 5_000);
|
||||||
|
testTimeout(() -> Thread.sleep(Long.MAX_VALUE, 999_999), 5_000);
|
||||||
|
|
||||||
|
for (final int millis : TRY_MILLIS) {
|
||||||
|
for (final int nanos : TRY_NANOS) {
|
||||||
|
testTimes(() -> Thread.sleep(millis, nanos), millis, 20_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testTimes(TestCase t, long millisMin, long millisMax) throws Exception {
|
||||||
|
long start = System.nanoTime();
|
||||||
|
t.run();
|
||||||
|
long end = System.nanoTime();
|
||||||
|
long duration = TimeUnit.NANOSECONDS.toMillis(end - start);
|
||||||
|
assertTrue(duration >= millisMin, "Duration " + duration + "ms, expected >= " + millisMin + "ms");
|
||||||
|
assertTrue(duration <= millisMax, "Duration " + duration + "ms, expected <= " + millisMax + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testTimeout(TestCase t, long millis) throws Exception {
|
||||||
|
Thread captThread = Thread.currentThread();
|
||||||
|
Thread watcher = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(millis);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
captThread.interrupt();
|
||||||
|
});
|
||||||
|
watcher.setDaemon(true);
|
||||||
|
watcher.start();
|
||||||
|
try {
|
||||||
|
t.run();
|
||||||
|
fail("Exited before timeout");
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
watcher.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testIAE(TestCase t, String msg) throws Exception {
|
||||||
|
try {
|
||||||
|
t.run();
|
||||||
|
fail("Should have thrown the IAE");
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
assertTrue(iae.getMessage().contains(msg),
|
||||||
|
"Thrown IAE does not contain the string: " + msg + " " + iae);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface TestCase {
|
||||||
|
void run() throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
68
test/micro/org/openjdk/bench/java/lang/ThreadSleep.java
Normal file
68
test/micro/org/openjdk/bench/java/lang/ThreadSleep.java
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package org.openjdk.bench.java.lang;
|
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@BenchmarkMode(Mode.AverageTime)
|
||||||
|
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||||
|
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
|
||||||
|
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
|
||||||
|
@Fork(3)
|
||||||
|
@State(Scope.Benchmark)
|
||||||
|
public class ThreadSleep {
|
||||||
|
|
||||||
|
@Param({"0",
|
||||||
|
"1",
|
||||||
|
"10",
|
||||||
|
"100",
|
||||||
|
"1000",
|
||||||
|
"10000",
|
||||||
|
"100000",
|
||||||
|
"1000000",
|
||||||
|
"10000000",
|
||||||
|
"100000000",
|
||||||
|
"1000000000"})
|
||||||
|
private int sleep;
|
||||||
|
|
||||||
|
private long millis;
|
||||||
|
private int nanos;
|
||||||
|
|
||||||
|
@Setup
|
||||||
|
public void setup() {
|
||||||
|
millis = TimeUnit.NANOSECONDS.toMillis(sleep);
|
||||||
|
nanos = (int)(sleep - TimeUnit.MILLISECONDS.toNanos(millis));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void millis() throws InterruptedException {
|
||||||
|
Thread.sleep(millis);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void millisNanos() throws InterruptedException {
|
||||||
|
Thread.sleep(millis, nanos);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue