1. Общ преглед
В тази статия ще разгледаме как да измерваме изминалото време в Java. Макар че това може да звучи лесно, има няколко подводни камъка, с които трябва да сме наясно.
Ще проучим стандартни Java класове и външни пакети, които предоставят функционалност за измерване на изминалото време.
2. Прости измервания
2.1. currentTimeMillis ()
Когато срещнем изискване за измерване на изминалото време в Java, можем да се опитаме да го направим по следния начин:
long start = System.currentTimeMillis(); // ... long finish = System.currentTimeMillis(); long timeElapsed = finish - start;
Ако погледнем кода, това е напълно логично. Получаваме времева марка в началото и получаваме друга времева марка, когато кодът приключи. Изминалото време е разликата между тези две стойности.
Въпреки това, резултатът може и ще бъде неточно, както System.currentTimeMillis () мерки за стенен часовник на времето. Времето на стенен часовник може да се промени по много причини, напр. Промяната на системното време може да повлияе на резултатите или скоковата секунда ще наруши резултата.
2.2. nanoTime ()
Друг метод в класа java.lang.System е nanoTime () . Ако разгледаме документацията за Java, ще намерим следното твърдение:
„Този метод може да се използва само за измерване на изминало време и не е свързан с каквото и да е друго понятие за системно или стенен часовник.“
Нека го използваме:
long start = System.nanoTime(); // ... long finish = System.nanoTime(); long timeElapsed = finish - start;
Кодът по същество е същият както преди. Единствената разлика е методът, използван за получаване на времеви марки - nanoTime () вместо currentTimeMillis () .
Нека също да отбележим, че nanoTime () очевидно връща времето в наносекунди. Следователно, ако изминалото време се измерва в различна времева единица, ние трябва да го преобразуваме съответно.
Например, за да преобразуваме в милисекунди, трябва да разделим резултата в наносекунди на 1.000.000.
Друг проблем с nanoTime () е, че въпреки че осигурява наносекундна прецизност, не гарантира наносекундна разделителна способност (т.е. колко често се актуализира стойността).
Въпреки това гарантира, че разделителната способност ще бъде поне толкова добра, колкото тази на currentTimeMillis () .
3. Java 8
Ако използваме Java 8 - можем да изпробваме новите класове java.time.Instant и java.time.Duration . И двете са неизменни, безопасни за нишки и използват свой собствен времеви мащаб, Java Time-Scale, както правят всички класове в новия API на java.time .
3.1. Времева скала на Java
Традиционният начин за измерване на времето е разделянето на деня на 24 часа от 60 минути от 60 секунди, което дава 86 400 секунди на ден. Слънчевите дни обаче не винаги са еднакво дълги.
Времевата скала на UTC всъщност позволява на деня да има 86.399 или 86.401 SI секунди. SI втора е научна „стандартна международна секунда“ и се определя от периоди на излъчване на атом цезий 133). Това е необходимо, за да се поддържа денят съобразен със Слънцето.
Времевата скала на Java разделя всеки календарен ден на точно 86 400 подразделения, известни като секунди . Няма високосни секунди.
3.2. Незабавен клас
В Instant класа представлява един миг на времевата линия. По принцип това е цифров клеймо от стандартната епоха на Java от 1970-01-01T00: 00: 00Z .
За да получим текущия клеймо, можем да използваме статичния метод Instant.now () . Този метод позволява предаване на незадължителен параметър Clock . Ако е пропуснато, той използва системния часовник в часовата зона по подразбиране.
Можем да съхраняваме началното и крайното време в две променливи, както в предишните примери. След това можем да изчислим времето, изминало между двата момента.
Можем допълнително да използваме класа Duration и това е между () метод, за да получим продължителността между два моментални обекта. И накрая, трябва да преобразуваме продължителността в милисекунди:
Instant start = Instant.now(); // CODE HERE Instant finish = Instant.now(); long timeElapsed = Duration.between(start, finish).toMillis();
4. Хронометър
Преминавайки към библиотеките, Apache Commons Lang предоставя клас StopWatch, който може да се използва за измерване на изминалото време.
4.1. Зависимост на Maven
Можем да получим най-новата версия, като актуализираме pom.xml:
org.apache.commons commons-lang3 3.7
Най-новата версия на зависимостта може да бъде проверена тук.
4.2. Измерване на изминалото време с хронометър
На първо място, трябва да получим екземпляр на класа и след това можем просто да измерим изминалото време:
StopWatch watch = new StopWatch(); watch.start();
След като стартираме часовник, можем да изпълним кода, който искаме да сравним и след това в края просто извикваме метода stop () . И накрая, за да получим действителния резултат, извикваме getTime () :
watch.stop(); System.out.println("Time Elapsed: " + watch.getTime()); // Prints: Time Elapsed: 2501
StopWatch има няколко допълнителни помощни метода, които можем да използваме, за да поставим на пауза или възобновим измерването си. Това може да е полезно, ако трябва да направим нашия бенчмарк по-сложен.
И накрая, нека отбележим, че класът не е безопасен за нишки.
5. Заключение
Има много начини за измерване на времето в Java. Покрихме много „традиционен“ (и неточен) начин, използвайки currentTimeMillis () . Освен това проверихме StopWatch на Apache Common и разгледахме новите класове, налични в Java 8.
Като цяло за прости и правилни измервания на изминалото време е достатъчен методът nanoTime () . Също така е по-кратък за въвеждане от currentTimeMillis () .
Нека отбележим обаче, че за правилното сравнение, вместо да измерваме времето ръчно, можем да използваме рамка като Java Microbenchmark Harness (JMH). Тази тема излиза извън обхвата на тази статия, но ние я разгледахме тук.
И накрая, както винаги, кодът, използван по време на дискусията, може да бъде намерен в GitHub.