Thread.sleep спит меньше указанного времени?

Я пробовал это:

for(int i =0; i<10; i++)
{
    long t1 = System.nanoTime();
    try{Thread.sleep(1000);}catch(Exception e){}
    double time = (System.nanoTime() - t1)/1000000;
    System.out.println(i+") "+time);
}

И вывод этого:

0) 987.0
1) 999.0
2) 999.0
3) 999.0
4) 999.0
5) 1000.0
6) 999.0
7) 997.0
8) 999.0
9) 999.0

Как видно, Thread.sleep() не совсем точен, так как засыпал на 1000 мс только 1 раз из 10 попыток.

Но я думал, что это займет больше времени, чем 1000 мс, так как потребуется некоторое время, чтобы вычислить значение t1 и time. Какая-то конкретная причина, по которой он спит меньше времени, а не больше, чем указано? (Мой компьютер точно не мега-суперкомпьютер, подаренный инопланетянами, чтобы вычислять значения t1 и time в отрицательное время! :P )

Кроме того, как я могу перевести поток в спящий режим на ровно 1000 мс?


person dryairship    schedule 09.01.2016    source источник
comment
Как указано в документах: Заставляет текущий исполняемый поток спать (временно прекращать выполнение) на указанное количество миллисекунд, в зависимости от точности и точности системных таймеров и планировщиков.   -  person PeterMmm    schedule 09.01.2016
comment
Ты не можешь, так что лучше пересмотри свои планы.   -  person Kayaman    schedule 09.01.2016
comment
Завершающий .0 происходит просто из метода toString() для Double: docs.oracle.com/javase/7/docs/api/java/lang/   -  person PeterMmm    schedule 09.01.2016
comment
Либо используйте внешнее оборудование, либо, если вы доверяете точности System.nanoTime() сна в течение 98% времени, которое вы хотите подождать, и продолжайте делать это с оставшимся временем, которое у вас осталось, пока вы не достигнете степени точности, которую считаете приемлемой для ваших нужд. К сожалению, это не сильно поможет вам с точностью менее миллисекунды. В качестве альтернативы, вообще не спите и загружайте процессор на 100%, выполняя while (System.nanoTime() < t1 + waitTime) {};   -  person flungo    schedule 09.01.2016
comment
@PeterMmm Я никогда так не называл!   -  person dryairship    schedule 09.01.2016
comment
Поскольку System.nanoTime() возвращает длинное значение, деление (System.nanoTime() - t1)/1000000 является длинным делением, дающим длинный результат. Присвоение результата двойной переменной не восстанавливает дробную часть.   -  person Thomas Kläger    schedule 09.01.2016
comment
@PriydarshiSingh, вы неявно вызываете toString(), когда объединяете строку с двойным. И, как упомянул Томанс, вы никогда не используете деление с плавающей запятой, потому что оно преобразуется в двойное только в последний момент перед присваиванием. Вместо этого попробуйте (System.nanoTime() - t1)/1000000.0.   -  person Sergei Tachenov    schedule 09.01.2016
comment
@SergeyTachenov Этого не происходит, когда я пытаюсь: double d = 32.323; System.out.println("d = "+d);   -  person dryairship    schedule 09.01.2016
comment
@PriydarshiSingh, в этом случае вы назначаете двойной литерал двойной переменной. Затем вы вызываете toString() и, конечно, это работает. В первом случае вы присваиваете значение long (вычисленное из выражения RHS) двойному числу, тем самым усекая дробную часть. Затем вы вызываете toString() и, конечно же, он печатает дробную часть, равную нулю. Прочтите литературу о том, как оцениваются выражения Java, может быть, даже JLS.   -  person Sergei Tachenov    schedule 09.01.2016
comment
Возможный дубликат Как долго это занимает одну секунду в Java? Измерение времени задержки в Java   -  person Svetlin Zarev    schedule 09.01.2016
comment
Можно поинтересоваться, зачем вам такой точный сон? Даже уже запущенный код может быть вытеснен и задержан ОС на неопределенное время. Вас беспокоят кумулятивные ошибки синхронизации по прошествии длительного времени? Вы можете исправить это, засыпая немного дольше или короче, или используя периодические обратные вызовы в исполнителе.   -  person Tamas Hegedus    schedule 09.01.2016


Ответы (2)


Из документации Java о сне

Переводит выполняющийся в данный момент поток в спящий режим (временно прекращает выполнение) на указанное количество миллисекунд, в зависимости от точности системных таймеров и планировщиков. Поток не теряет права собственности на какие-либо мониторы.

Поскольку в документации говорится, что сон зависит от базовой системы, это может не работать должным образом там, где требуется точность.

Хорошей альтернативой будет ScheduledExecutorService. Ниже приведены некоторые фрагменты кода о том, как его использовать:

<сильный>1. Создать ScheduledExecutorService

public static ScheduledExecutorService createScheduledExecutorService(Runnable command){
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleAtFixedRate(command, 0, 1, TimeUnit.SECONDS);
        return scheduledExecutorService;
    }

Изменить: Здесь я жестко запрограммировал начальную задержку со значением 0, период с 1 и TimeUnit Seconds. Подробнее о scheduleAtFixedRate.

<сильный>2. Создайте свою выполнимую задачу

public class Work implements Runnable{

 @Override
 public void run() {
    //Do Some thing here.
 }

}

<сильный>3. Запустить

public class Main {
    public static void main(String[] args) 
    {
        ExecutorServiceFactory.createScheduledExecutorService(new Work());
    }
}
person Somnath Musib    schedule 09.01.2016
comment
Где я должен написать время задержки? - person dryairship; 10.01.2016

Используется ScheduledExecutorService . это хорошо для вашей программы, потому что вы не используете никаких потоков.

    ScheduledExecutorService as= Executors.newSingleThreadScheduledExecutor();
    as.scheduleAtFixedRate(new ,i,0,TimeUnit.MINUTES);
person Waseem Saeed    schedule 09.01.2016
comment
new что? Что я должен создать, используя new? - person dryairship; 09.01.2016