← Назад к вопросам

Что используешь для планирования задач

1.0 Junior🔥 111 комментариев
#Soft Skills и карьера

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Планирование задач в Java

Что такое планирование задач

Планирование задач (Task Scheduling) — это запуск кода в определённое время или через определённые интервалы. Примеры: отправка email'ов, синхронизация данных, очистка кэша, резервные копии.

1. ScheduledExecutorService (стандартное решение)

Это встроенный в Java способ, использует пул потоков с таймерами.

import java.util.concurrent.*;

// Создание пула для планирования
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

// Запуск задачи один раз через 5 секунд
scheduler.schedule(() -> {
    System.out.println("Задача выполнена!");
}, 5, TimeUnit.SECONDS);

// Повторяющаяся задача: первый раз через 2 сек, затем каждые 3 сек
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(() -> {
    System.out.println("Повторяющаяся задача: " + LocalDateTime.now());
}, 2, 3, TimeUnit.SECONDS);

// Задача с фиксированной задержкой (3 сек между окончанием и следующим запуском)
scheduler.scheduleWithFixedDelay(() -> {
    System.out.println("Задача с задержкой");
    try { Thread.sleep(1000); } catch (InterruptedException e) {}
}, 2, 3, TimeUnit.SECONDS);

// Отмена задачи
future.cancel(true);

// Корректное завершение
scheduler.shutdown();
if (!scheduler.awaitTermination(10, TimeUnit.SECONDS)) {
    scheduler.shutdownNow();
}

Типы планирования:

  • schedule() — один раз
  • scheduleAtFixedRate() — каждые N времени (независимо от длительности задачи)
  • scheduleWithFixedDelay() — каждые N времени после окончания задачи

2. Spring @Scheduled (рекомендуемо для Spring приложений)

Самый удобный способ в Spring приложениях.

import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Configuration
@EnableScheduling  // Включить поддержку @Scheduled
public class SchedulingConfig {
}

@Service
public class TaskScheduler {
    
    // Запуск каждые 5 секунд (5000 миллисекунд)
    @Scheduled(fixedRate = 5000)
    public void taskWithFixedRate() {
        System.out.println("Задача каждые 5 секунд: " + LocalDateTime.now());
    }
    
    // Задержка 5 сек между окончанием одной и началом следующей
    @Scheduled(fixedDelay = 5000)
    public void taskWithFixedDelay() {
        System.out.println("Задача с задержкой: " + LocalDateTime.now());
        try { Thread.sleep(2000); } catch (InterruptedException e) {}
    }
    
    // Первый запуск через 10 сек, затем каждые 5 сек
    @Scheduled(initialDelay = 10000, fixedRate = 5000)
    public void taskWithInitialDelay() {
        System.out.println("Задача с отложенным стартом");
    }
    
    // По cron выражению (как в Linux)
    // Каждый день в 12:00
    @Scheduled(cron = "0 0 12 * * *")
    public void dailyTask() {
        System.out.println("Дневная задача в 12:00");
    }
    
    // Каждый понедельник в 9:00
    @Scheduled(cron = "0 0 9 ? * MON")
    public void weeklyTask() {
        System.out.println("Еженедельная задача");
    }
    
    // По расписанию из конфига
    @Scheduled(cron = "${app.schedule.cleanup-cron}")
    public void configuredTask() {
        System.out.println("Задача из конфига");
    }
}

Синтаксис cron:

┌───────────── seconds (0 - 59)
│ ┌───────────── minutes (0 - 59)
│ │ ┌───────────── hours (0 - 23)
│ │ │ ┌───────────── day of month (1 - 31)
│ │ │ │ ┌───────────── month (1 - 12)
│ │ │ │ │ ┌───────────── day of week (0 - 6)
│ │ │ │ │ │
│ │ │ │ │ │
* * * * * *

Примеры cron:

0 0 12 * * *     — 12:00 каждый день
0 0 0 * * *      — 00:00 каждый день (полночь)
0 0 */6 * * *    — каждые 6 часов
0 */15 * * * *   — каждые 15 минут
0 0 9 ? * MON    — 9:00 по понедельникам
0 0 9 1 * ?      — 9:00 первого числа каждого месяца
30 5 10 * * *    — 10:05:30 каждый день

3. Quartz Scheduler (для сложных сценариев)

Мощная библиотека для продвинутого планирования.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
import org.quartz.*;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

// Определение Job'а
@Component
public class EmailJob extends QuartzJobBean {
    
    @Autowired
    private EmailService emailService;
    
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        try {
            JobDataMap dataMap = context.getJobDetail().getJobDataMap();
            String email = dataMap.getString("email");
            
            emailService.sendEmail(email);
            System.out.println("Email отправлен на " + email);
        } catch (Exception e) {
            throw new JobExecutionException("Ошибка отправки email", e);
        }
    }
}

// Конфигурация Job'а
@Configuration
public class QuartzConfig {
    
    @Bean
    public JobDetail emailJobDetail() {
        return JobBuilder.newJob(EmailJob.class)
            .withIdentity("emailJob")
            .storeDurably()
            .usingJobData("email", "user@example.com")
            .build();
    }
    
    @Bean
    public Trigger emailJobTrigger(JobDetail emailJobDetail) {
        return TriggerBuilder.newTrigger()
            .forJob(emailJobDetail)
            .withIdentity("emailTrigger")
            .withSchedule(
                CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
            )
            .build();
    }
}

// Использование
@Service
public class JobSchedulingService {
    
    @Autowired
    private Scheduler scheduler;
    
    public void scheduleJob(String email) throws SchedulerException {
        JobDataMap dataMap = new JobDataMap();
        dataMap.put("email", email);
        
        JobDetail job = JobBuilder.newJob(EmailJob.class)
            .withIdentity("emailJob-" + email)
            .usingJobData(dataMap)
            .build();
        
        Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity("emailTrigger-" + email)
            .withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(12, 0))
            .build();
        
        scheduler.scheduleJob(job, trigger);
    }
}

4. Spring Task Executor (для параллельного выполнения)

@Configuration
@EnableAsync  // Включить асинхронность
public class AsyncConfig {
    
    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("Async-");
        executor.initialize();
        return executor;
    }
}

@Service
public class AsyncTaskService {
    
    @Async("taskExecutor")
    public void asyncTask(String taskName) {
        System.out.println("Асинхронная задача: " + taskName);
        // Выполняется в отдельном потоке
    }
    
    @Async
    public Future<String> asyncTaskWithResult(String input) throws InterruptedException {
        Thread.sleep(2000);
        return new AsyncResult<>("Результат: " + input);
    }
}

// Использование
@Service
public class MyService {
    
    @Autowired
    private AsyncTaskService asyncService;
    
    public void startAsyncWork() {
        asyncService.asyncTask("task-1");
        asyncService.asyncTask("task-2");
        // Оба выполняются параллельно
        
        Future<String> result = asyncService.asyncTaskWithResult("data");
        try {
            System.out.println(result.get());  // Ждёт результат
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

5. Comparision таблица

┌────────────────────┬──────────┬──────────┬──────────────┐
│ Способ             │ Простота │ Гибкость │ Персистентность │
├────────────────────┼──────────┼──────────┼──────────────┤
│ ScheduledExecutor  │ Средняя  │ Средняя  │ В памяти     │
│ @Scheduled         │ Высокая  │ Средняя  │ В памяти     │
│ Quartz             │ Низкая   │ Очень вы │ В БД         │
│ @Async            │ Высокая  │ Низкая   │ В памяти     │
└────────────────────┴──────────┴──────────┴──────────────┘

Практические примеры

Очистка кэша:

@Service
public class CacheService {
    
    @Autowired
    private CacheManager cacheManager;
    
    @Scheduled(cron = "0 0 2 * * *")  // 2:00 AM каждый день
    public void clearCache() {
        cacheManager.getCacheNames().forEach(name -> 
            cacheManager.getCache(name).clear()
        );
        System.out.println("Кэш очищен");
    }
}

Синхронизация данных:

@Service
public class SyncService {
    
    @Autowired
    private ExternalApiClient apiClient;
    
    @Autowired
    private DataRepository dataRepository;
    
    @Scheduled(fixedRate = 300000)  // Каждые 5 минут
    public void syncData() {
        try {
            List<Data> externalData = apiClient.fetchData();
            dataRepository.saveAll(externalData);
            System.out.println("Данные синхронизированы");
        } catch (Exception e) {
            System.err.println("Ошибка синхронизации: " + e.getMessage());
        }
    }
}

Отправка отчётов:

@Service
public class ReportService {
    
    @Autowired
    private EmailService emailService;
    
    @Scheduled(cron = "0 0 9 ? * MON")  // 9:00 по понедельникам
    public void sendWeeklyReport() {
        String report = generateReport();
        emailService.sendEmail("admin@company.com", "Еженедельный отчёт", report);
    }
    
    private String generateReport() {
        // Логика генерации отчёта
        return "Report...";
    }
}

Best Practices

  1. Обработка ошибок:
@Scheduled(fixedRate = 5000)
public void robustTask() {
    try {
        // Код задачи
    } catch (Exception e) {
        // Логировать, но не падать
        logger.error("Task failed", e);
    }
}
  1. Логирование:
@Service
public class ScheduledTasks {
    private static final Logger logger = LoggerFactory.getLogger(ScheduledTasks.class);
    
    @Scheduled(fixedRate = 5000)
    public void task() {
        logger.info("Task started at {}", LocalDateTime.now());
        // ...
        logger.info("Task completed");
    }
}
  1. Настройка из конфига:
app.task.sync-interval=300000
app.task.cleanup-cron=0 0 2 * * *
app.task.enabled=true
@Service
public class ConfigurableScheduler {
    
    @Value("${app.task.enabled:true}")
    private boolean tasksEnabled;
    
    @Scheduled(fixedRateString = "${app.task.sync-interval}")
    public void conditionalTask() {
        if (!tasksEnabled) return;
        // Логика задачи
    }
}

Рекомендации

  • Простые задачи → @Scheduled
  • Сложная логика → Quartz
  • Низкоуровневый контроль → ScheduledExecutorService
  • Параллельное выполнение → @Async
  • Нужна персистентность → Quartz с БД

Для большинства Spring приложений достаточно @Scheduled с cron выражениями.