Я не использую ScheduledExecutorService.Я хочу простой способ решения этой проблемы.
У вас уже есть ответ: Платформа Executors была добавлена в Java, чтобы быть простым способом для решения этой проблемы.Исполнители абстрагируются от сложных и запутанных деталей обработки фоновых задач и потоков.
Вы должны использовать ScheduledExecutorService
для своих целей.
Исполнители могут показаться пугающими на первый взгляд, но их использование на практике довольно просто.Прочтите руководство по Oracle.Затем посмотрите примеры кода в Stack Overflow, других блогах и т. Д.
Совет. Поиск переполнения стека осуществляется с помощью внешней поисковой системы, такой как DuckDuckGo / Bing / Google, с критерием site:StackOverflow.com
.Встроенная функция поиска в переполнении стека анемична и смещена в сторону вопросов, а не ответов.
Определите службу исполнителя, поддерживаемую пулом потоков.
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
Определите вашу задачу для запуска как Runnable
.
Runnable task = new Runnable() {
@Override
public void run () {
… stuff to do goes here
}
}
Передать объект Runnable
в службу исполнителя.Расскажи как часто бегать.Вы также проходите начальную задержку;здесь мы проходим ноль, чтобы начать немедленно.
scheduledExecutorService.scheduleAtFixedRate( task , 0 , 2 , TimeUnit.HOURS );
Совет. Об использовании ScheduledExecutorService
(SES): две важные вещи:
- Всегда корректно выключайте SES, когда он больше не нужен или когда ваше приложениевыходы.В противном случае пул потоков может выжить и продолжать работать, как зомби.
- Всегда отлавливать все исключения и, возможно, ошибки в задаче, передаваемой в SES.Если возникает какое-либо исключение или ошибка, чтобы достигнуть SES, вся дальнейшая работа SES прекращается.Остановка молчит, без предупреждения, без журналов.
Поэтому измените Runnable
, показанный выше.
Runnable task = new Runnable() {
@Override
public void run () {
try {
… stuff to do goes here
} catch ( Exception e ) {
… Handle any unexpected exceptions bubbling up to avoid silently killing your executor service.
}
}
}
Совет: Никогда не используйте ужасные старые классы даты и времени, такие как Date
, Calendar
SimpleDateFormat
.Они были вытеснены несколько лет назад современными java.time классами.
Совет. При записи момента в имени файла следуйте стандарту ISO 8601 для представления значений времени и даты в виде текста.
Эти форматы по умолчанию используются в классах java.time при разборе или генерации строк.
Для имен файлов вы можете использовать альтернативные «базовые» форматы, определенные в стандарте. Basic означает, что использование разделителей сведено к минимуму.
Обязательно избегайте обратной косой черты, прямой косой черты и символов двоеточия.Это запрещено в файловых системах DOS / Windows, Unix и macOS / iOS, соответственно.
Совет. Не пишите собственный CSV-код.Используйте библиотеку Apache Commons CSV для чтения и записи файлов CSV или табуляции.Это хорошо работает по моему опыту.
Пример приложения
Вот целый пример, содержащийся в одном файле .java
.
У нас есть несколько объектов нашего маленького класса Event
.При каждом запланированном запуске мы обновляем метку времени для каждого объекта Event
.Затем мы записываем все переменные-члены всех этих Event
объектов в текстовый файл в формате CSV, используя библиотеку Apache Commons CSV.Каждый прогон определяется объектом Runnable
.
Прогоны планируются с помощью ScheduledExecutorService
, поддерживаемого пулом потоков с одним потоком.
В реальной работе я бы не сжал всеэто в один .java
файл.Но в этом случае получится хорошее компактное демо.
package com.basilbourque.example;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ExportToCsv {
public static void main ( String[] args ) {
ExportToCsv app = new ExportToCsv();
app.doIt();
}
private void doIt () {
System.out.println( "TRACE - doIt running at " + ZonedDateTime.now() );
List< Event > events = List.of(
new Event( UUID.randomUUID() , "alpha" , Instant.now() ) ,
new Event( UUID.randomUUID() , "beta" , Instant.now() ) ,
new Event( UUID.randomUUID() , "gamma" , Instant.now() )
);
Runnable task = new Runnable() {
@Override
public void run () {
// Nest all this stuff of your `run` method into a `try-catch( Exception e )` to avoid having your executor cease silently.
Instant start = Instant.now();
System.out.print( "TRACE - Runnable starting at " + start + ". " );
// Update the moment recorded in each `Event` object.
events.forEach( ( event ) -> event.update() );
// Export to CSV. Using “Apache Commons CSV” library. https://commons.apache.org/proper/commons-csv/
// Get current moment in UTC. Lop off the seconds and fractional second. Generate text without delimiters.
String dateTimeLabel = OffsetDateTime.now( ZoneOffset.UTC ).truncatedTo( ChronoUnit.MINUTES ).format( DateTimeFormatter.ofPattern( "uuuuMMdd'T'HHmmX" , Locale.US ) );
String fileNamePath = "myCsv_" + dateTimeLabel + ".csv";
try ( // Try-with-resources syntax automatically closes any passed objects implementing `AutoCloseable`, even if an exception is thrown.
BufferedWriter writer = new BufferedWriter( new FileWriter( fileNamePath ) ) ;
CSVPrinter csvPrinter = new CSVPrinter( writer , CSVFormat.DEFAULT.withHeader( "Id" , "Name" , "When" ) ) ;
) {
for ( Event event : events ) {
csvPrinter.printRecord( event.id , event.name , event.when );
}
csvPrinter.flush();
} catch ( IOException e ) {
// TODO: Handle i/o exception when creating or writing to file in storage.
e.printStackTrace();
}
Instant stop = Instant.now() ;
System.out.println( "Runnable ending its run at " + start + ". Duration: " + Duration.between( start , stop ) + ".");
}
};
// Schedule this task. Currently set to run every two minutes, ending after 20 minutes. Adjust as desired.
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); // Using a single thread here, as we have only a single series of tasks to be executed, no multi-tasking.
try {
scheduledExecutorService.scheduleAtFixedRate( task , 0 , 2 , TimeUnit.MINUTES ); // Schedule our task to run every so often.
try {
Thread.sleep( TimeUnit.MINUTES.toMillis( 20 ) ); // Sleep this main thread for a while to let our task running on the background thread do its thing a few times.
} catch ( InterruptedException e ) {
System.out.println( "TRACE - Our main thread was woken earlier than expected, and interrupted our run. " );
e.printStackTrace();
}
} finally {
System.out.println( "Shutting down the scheduledExecutorService at " + ZonedDateTime.now() ); // Generally best to log in UTC, `Instant.now()`.
scheduledExecutorService.shutdown(); // Always shutdown your executor, as it may otherwise survive your app exiting, becoming a zombie, continuing to run endlessly.
}
System.out.println( "App running on main thread ending at " + Instant.now() + "." );
}
class Event {
public UUID id;
public String name;
public Instant when;
public Event ( UUID id , String name , Instant when ) {
this.id = id;
this.name = name;
this.when = when;
}
public void update () {
this.when = Instant.now();
}
}
}
При запуске.
TRACE - не работает в 2018-09-24T20: 16: 25.794081-07: 00[America / Los_Angeles]
TRACE - Работает с 2018-09-25T03: 16: 25.832025Z.Runnable заканчивает свой пробег в 2018-09-25T03: 16: 25.832025Z.Продолжительность: PT0.025342S.
TRACE - Запуск с 2018-09-25T03: 18: 25.829634Z.Runnable заканчивает свой пробег в 2018-09-25T03: 18: 25.829634Z.Продолжительность: PT0.001121S.
Файлы, показанные на этом снимке экрана.
![enter image description here](https://i.stack.imgur.com/x4Way.png)