Runnable
Определите работу, которую вам нужно сделать, как Runnable
.
Затем используйте Scheduled ExecutorService
для запуска каждую минуту или около того, сверяя текущую дату с целевой датой.Если обратный отсчет увеличился, обновите отображение в графическом интерфейсе.Если нет, ничего не делайте, пусть Runnable
снова запустится через минуту.
См. Руководство по Oracle для исполнителей .
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor(); // Use a single thread, as we need this to run sequentially, not in parallel.
ses.scheduleWithFixedDelay( r , 0L , 1L , TimeUnit.MINUTES ); // Pass the `Runnable`, an initial delay, a count to wait between runs, and the unit of time for those two other number arguments.
Было бы более эффективно планировать новый Runnable
каждый раз, чем автоматическое повторение, поэтомуВ качестве задержки установите расчетное количество времени до следующей полуночи.Таким образом, исполнитель будет спать весь день, а не бегать каждую минуту.Но такой подход может потерпеть неудачу, если часы пользователя обновляются до существенно отличающегося текущего времени или если текущий часовой пояс пользователя меняется (если вы используете настройку по умолчанию, а не устанавливаете ее явно).Учитывая, как мало нужно сделать Runnable
(просто проверьте текущую дату и рассчитайте оставшиеся дни), нет никаких практических причин не просто запускать его каждую минуту или две (как бы долго вы ни были минимально терпимы к свежему обновлению для пользователя - бизнеса)политика управления вашим приложением).
LocalDate
Класс LocalDate
представляет значение только для даты без времени суток и без часовой пояс или смещение от UTC .
Часовой пояс имеет решающее значение при определении даты.В любой момент времени дата меняется по всему земному шару в зависимости от зоны.Например, через несколько минут после полуночи в Париж Франция - это новый день, в то время как "вчера" в Монреаль Квебек .
Если часовой пояс не указан,JVM неявно применяет свой текущий часовой пояс по умолчанию.Это значение по умолчанию может измениться в любой момент во время выполнения (!), Поэтому ваши результаты могут отличаться.Лучше явно указать желаемый / ожидаемый часовой пояс в качестве аргумента.
Укажите правильное имя часового пояса в формате Continent/Region
, например America/Montreal
, Africa/Casablanca
,или Pacific/Auckland
.Никогда не используйте 2-4 буквенные сокращения, такие как EST
или IST
, так как они не истинные часовые пояса, не стандартизированы и даже не уникальны (!).
ZoneId z = ZoneId.of( "Asia/Kolkata" ); // Or ZoneId.systemDefault() to rely on the JVM’s current default time zone.
LocalDate today = LocalDate.now( z );
Пример
Вот полный пример.Для получения дополнительной информации, поиск переполнения стека.Использование ScheduledExecutorService
уже много раз охватывалось.
package work.basil.example;
import java.time.Instant;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class DailyCountdown implements Runnable {
private LocalDate dueDate;
private Long daysRemaining;
public DailyCountdown ( LocalDate dueDate ) {
this.dueDate = dueDate;
}
@Override
public void run () {
try {
System.out.println( "DEBUG - Running the DailyCountdown::run method at " + Instant.now() );
ZoneId z = ZoneId.of( "America/Montreal" ); // Or ZoneId.systemDefault() to rely on the JVM’s current default time zone.
LocalDate today = LocalDate.now( z );
Long count = ChronoUnit.DAYS.between( today , this.dueDate );
if ( Objects.isNull( this.daysRemaining ) ) {
this.daysRemaining = ( count - 1 );
}
if ( this.daysRemaining.equals( count ) ) {
// Do nothing.
} else {
// … Schedule on another thread for the GUI to update with the new number.
this.daysRemaining = count;
}
} catch ( Exception e ) {
// Log this unexpected exception, and notify sysadmin.
// Any uncaught exception reaching the scheduled executor service would have caused it to silently halt any further scheduling.
}
}
public static void main ( String[] args ) {
// Put this code where ever appropriate, when setting up your GUI after the app launches.
Runnable r = new DailyCountdown( LocalDate.of( 2018 , Month.FEBRUARY , 15 ) );
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
ses.scheduleWithFixedDelay( r , 0L , 1L , TimeUnit.MINUTES );
// Be sure to gracefully shutdown the ScheduledExecutorService when your program is stopping. Otherwise, the executor may continue running indefinitely on the background thread.
try {
Thread.sleep( TimeUnit.MINUTES.toMillis( 7 ) ); // Sleep 7 minutes to let the background thread do its thing.
} catch ( InterruptedException e ) {
System.out.println( "The `main` thread was woken early from sleep." );
}
ses.shutdown();
System.out.println( "App is exiting at " + Instant.now() ) ;
}
}