Ответ
Да, для этого в Java используется ScheduledExecutorService из пакета java.util.concurrent. Он позволяет планировать задачи на выполнение с задержкой или периодически, используя выделенный пул потоков.
Пример создания и использования:
import java.util.concurrent.*;
// Создание планировщика с пулом из 4 потоков
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);
// Запуск однократной задачи с задержкой в 5 секунд
scheduler.schedule(
() -> System.out.println("Task executed after delay"),
5,
TimeUnit.SECONDS
);
// Периодическое выполнение задачи каждые 2 секунды, начиная через 1 секунду
scheduler.scheduleAtFixedRate(
() -> System.out.println("Periodic task"),
1,
2,
TimeUnit.SECONDS
);
// Периодическое выполнение с фиксированной паузой *между* окончанием задачи и началом следующей
scheduler.scheduleWithFixedDelay(
() -> System.out.println("Task with fixed delay"),
1,
2,
TimeUnit.SECONDS
);
// Корректное завершение работы планировщика
scheduler.shutdown();
Ключевые особенности и best practices:
- Управление ресурсами: Пул потоков позволяет контролировать количество одновременно выполняемых задач и избегать создания новых потоков для каждой.
- Гибкость планирования: Поддержка
schedule,scheduleAtFixedRate(фиксированный интервал) иscheduleWithFixedDelay(фиксированная пауза после окончания задачи). - Важность завершения: Обязательно вызывать
shutdown()илиshutdownNow()для освобождения ресурсов JVM, иначе потоки останутся активными. - Обработка исключений: Исключения, выброшенные в задаче, прерывают её выполнение, но не завершают работу планировщика. Необходимо обрабатывать их внутри задачи.
Ответ 18+ 🔞
А, ну это ж классика, блядь! Опять твоя программа должна что-то делать по расписанию, как поезд, который, сука, вечно опаздывает, но в коде-то он должен ходить точно! В Java для этого есть отличная штука — ScheduledExecutorService. Это не какой-нибудь костыль на Thread.sleep(), который только и умеет, что спать, блядь, а потом просыпаться и охуевать от того, что всё сломалось.
Смотри, как это делается по-взрослому, без самодеятельности:
import java.util.concurrent.*;
// Создаём планировщик. Цифра 4 — это сколько у него «рабов»-потоков в пуле.
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);
// Запустить задачу ОДИН РАЗ, но через 5 секунд. Типа "напомнить мне выключить чайник".
scheduler.schedule(
() -> System.out.println("Task executed after delay"),
5,
TimeUnit.SECONDS
);
// А это уже по-серьёзному: задача будет стартовать каждые 2 секунды, начиная через секунду.
// Интервал считается от НАЧАЛА предыдущей задачи. Если задача выполняется долго — может наложиться, как говно на вентилятор.
scheduler.scheduleAtFixedRate(
() -> System.out.println("Periodic task"),
1,
2,
TimeUnit.SECONDS
);
// А вот это умнее: интервал считается между ОКОНЧАНИЕМ одной задачи и НАЧАЛОМ следующей.
// То есть пауза гарантирована, даже если задача ебёт мозги дольше планируемого.
scheduler.scheduleWithFixedDelay(
() -> System.out.println("Task with fixed delay"),
1,
2,
TimeUnit.SECONDS
);
// И ВАЖНЕЙШЕЕ, блядь! Не забудь его прибить, когда надобность отпадёт!
// Иначе потоки так и останутся висеть, как призраки в оперативке, жрущие твои ресурсы.
scheduler.shutdown();
На что смотреть, чтобы не обосраться, ёпта:
- Потоки: Ты сам решаешь, сколько их в пуле. Сделаешь один — задачи будут ждать в очереди, как в поликлинике. Сделаешь сто — можешь выебать систему.
- Исключения: Если внутри твоей лямбды вылетит необработанный
Exception— задача просто тихо сдохнет, а планировщик будет дальше жить и радоваться. Так что оборачивай всё в try-catch, если не хочешь, чтобы твоё расписание потихоньку развалилось, и ты потом неделю искал, почему же оно не пашет. - Завершение:
shutdown()— это вежливое «всё, ребята, расходимся».shutdownNow()— это уже «всем нахуй немедленно, кто не успел — тот опоздал». Выбирай по обстановке.
Вот и вся магия. Никаких велосипедов, всё уже придумали до нас, только пользуйся.