Week 8 Day 3 — Scheduling and Async
Goal
Today I want to understand scheduling and asynchronous execution in Spring.
Main questions:
- What is scheduling?
- What is
@Scheduled? - What is
@EnableScheduling? - What is fixed rate?
- What is fixed delay?
- What is cron?
- What is
@Async? - What is
@EnableAsync? - What is an executor?
- What is a thread pool?
- What does async actually mean?
- What are common production traps?
- What is important for the Spring Professional exam?
1. Quick Review from Week 8 Day 2
In Day 2, I learned:
- Spring events signal that something happened.
ApplicationEventPublisherpublishes events.@EventListenerreceives events.- Events are synchronous by default.
@TransactionalEventListener(AFTER_COMMIT)runs after successful commit.@Asynccan make event listeners asynchronous.- Spring events are in-process, not durable messaging.
- Use Kafka/RabbitMQ/outbox for durable distributed messaging.
Memory sentence:
Spring events are local in-process events, not durable messaging.
Today I learn two related topics:
scheduled execution
asynchronous execution
2. Big Picture
Spring supports two common background execution styles.
Scheduling
Run something at a planned time or interval.
Examples:
run every 5 minutes
run every night at 02:00
run every Monday morning
clean expired tokens every hour
send daily report every day
Spring annotation:
@Scheduled
Async
Run something in another thread.
Examples:
send email in background
generate PDF in background
call external API without blocking caller
process notification asynchronously
Spring annotation:
@Async
Memory sentence:
@Scheduled = when should it run?
@Async = should it run in another thread?
3. Scheduling vs Async
| Topic | Scheduling | Async |
|---|---|---|
| Main question | When should the task run? | Which thread should run the method? |
| Annotation | @Scheduled | @Async |
| Enable with | @EnableScheduling | @EnableAsync |
| Common use | periodic jobs | background execution |
| Example | clean expired tokens every hour | send email in another thread |
Memory sentence:
Scheduling is about time. Async is about threads.
4. What Is @Scheduled?
@Scheduled tells Spring:
Run this method automatically according to a schedule.
Example:
@Component
public class TokenCleanupJob {
@Scheduled(fixedDelay = 60_000)
public void cleanExpiredTokens() {
System.out.println("Cleaning expired tokens...");
}
}
This runs repeatedly with a fixed delay.
Important:
The method should usually return void.
The method should not require arguments.
Spring calls it automatically.
Memory sentence:
@Scheduledmakes Spring run a method periodically.
5. Enable Scheduling
To use @Scheduled, enable scheduling:
@Configuration
@EnableScheduling
public class SchedulingConfig {
}
In a Spring Boot app, this config can be placed on any configuration class.
Example:
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Memory sentence:
@EnableSchedulingactivates scheduled method processing.
6. Basic Scheduled Job
@Component
public class DailyReportJob {
@Scheduled(fixedRate = 10_000)
public void generateReport() {
System.out.println("Generating report...");
}
}
This means:
Run this method every 10 seconds.
But the exact behavior depends on fixed rate vs fixed delay.
Memory sentence:
A scheduled job is a Spring bean method executed automatically.
7. Fixed Rate
fixedRate means:
Start a new execution at a regular interval measured from the start time of the previous execution.
Example:
@Scheduled(fixedRate = 5_000)
public void runEveryFiveSeconds() {
System.out.println("Running...");
}
Timeline:
00:00 start
00:05 start
00:10 start
00:15 start
Important:
fixedRate measures start-to-start.
Memory sentence:
Fixed rate means start every X milliseconds.
8. Fixed Rate Trap
Imagine:
@Scheduled(fixedRate = 5_000)
public void longJob() {
// takes 8 seconds
}
Question:
What happens if the job takes longer than the fixed rate?
Answer:
It depends on the scheduler/thread pool configuration.
With a single scheduler thread, executions do not run truly in parallel.
With a pool, overlapping can happen if configured that way.
Production warning:
Do not use fixedRate blindly for long-running jobs.
Memory sentence:
Fixed rate can be dangerous if the job takes longer than the interval.
9. Fixed Delay
fixedDelay means:
Wait X milliseconds after the previous execution finishes.
Example:
@Scheduled(fixedDelay = 5_000)
public void runWithDelay() {
System.out.println("Running...");
}
Timeline if job takes 2 seconds:
00:00 start
00:02 finish
00:07 start
00:09 finish
00:14 start
Important:
fixedDelay measures finish-to-next-start.
Memory sentence:
Fixed delay waits after the previous run finishes.
10. Fixed Rate vs Fixed Delay
| Topic | fixedRate | fixedDelay |
|---|---|---|
| Measures from | start time | finish time |
| Meaning | run every X ms | wait X ms after completion |
| Best for | regular polling rhythm | avoid overlap / give rest after work |
| Risk | overlap or pressure if job is slow | schedule shifts if job is slow |
Memory sentence:
fixedRate = start to start.
fixedDelay = finish to next start.
11. Initial Delay
initialDelay means:
Wait before first execution.
Example:
@Scheduled(initialDelay = 10_000, fixedDelay = 60_000)
public void runAfterStartupDelay() {
System.out.println("First run after 10 seconds, then every 60 seconds after finish.");
}
Use case:
give application time to start
avoid running job immediately
wait for dependencies to be ready
Memory sentence:
initialDelaydelays the first scheduled run.
12. Time Unit
Modern Spring supports timeUnit.
Example:
@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.MINUTES)
public void cleanExpiredSessions() {
}
Without timeUnit, values are often milliseconds.
Example:
@Scheduled(fixedDelay = 5000)
means:
5000 milliseconds = 5 seconds
Memory sentence:
Without clear time units, scheduled values are easy to misread.
13. Cron
Cron is used for calendar-like schedules.
Example:
@Scheduled(cron = "0 0 2 * * *")
public void runEveryNightAtTwo() {
System.out.println("Nightly job");
}
Meaning:
Run every day at 02:00.
Spring cron format commonly has 6 fields:
second minute hour day-of-month month day-of-week
Example:
0 0 2 * * *
Means:
second = 0
minute = 0
hour = 2
every day of month
every month
every day of week
Memory sentence:
Cron is for calendar-based schedules.
14. Common Cron Examples
Every day at 02:00:
@Scheduled(cron = "0 0 2 * * *")
Every hour:
@Scheduled(cron = "0 0 * * * *")
Every Monday at 08:00:
@Scheduled(cron = "0 0 8 * * MON")
Every 15 minutes:
@Scheduled(cron = "0 */15 * * * *")
Memory sentence:
Cron is powerful but easy to misread.
15. Cron Time Zone
You can specify a time zone:
@Scheduled(cron = "0 0 2 * * *", zone = "Europe/Berlin")
public void runAtBerlinTwoAm() {
}
Why important?
servers may run in UTC
business schedule may be Europe/Berlin
daylight saving time can matter
Memory sentence:
Always think about time zone for cron jobs.
16. Scheduled Method Rules
Scheduled methods should usually:
be no-argument methods
return void or ignore return value
be on Spring-managed beans
not rely on request context
handle exceptions carefully
not run too long without monitoring
Bad:
@Scheduled(fixedDelay = 5000)
public String runJob(String input) {
return "done";
}
Better:
@Scheduled(fixedDelay = 5000)
public void runJob() {
}
Memory sentence:
Scheduled methods are called by Spring, not by a normal controller request.
17. Scheduled Bean Must Be Managed by Spring
Works:
@Component
public class CleanupJob {
@Scheduled(fixedDelay = 60_000)
public void clean() {
}
}
Does not work:
CleanupJob job = new CleanupJob();
because Spring does not manage that object.
Memory sentence:
@Scheduledworks on Spring beans.
18. Scheduled Job Exception Trap
If a scheduled method throws an exception:
@Scheduled(fixedDelay = 60_000)
public void syncExternalSystem() {
throw new RuntimeException("External system failed");
}
Problem:
job execution fails
logs may contain error
future execution behavior depends on scheduler/error handling
important work may be skipped or repeatedly fail
Better:
@Scheduled(fixedDelay = 60_000)
public void syncExternalSystem() {
try {
externalSyncService.sync();
} catch (Exception exception) {
log.error("External sync failed", exception);
}
}
Memory sentence:
Scheduled jobs should handle and log exceptions carefully.
19. Do Not Put Big Logic Directly in Job Class
Bad:
@Component
public class ReportJob {
@Scheduled(cron = "0 0 2 * * *")
public void generateReports() {
// 300 lines of business logic
}
}
Better:
@Component
public class ReportJob {
private final ReportService reportService;
public ReportJob(ReportService reportService) {
this.reportService = reportService;
}
@Scheduled(cron = "0 0 2 * * *")
public void generateReports() {
reportService.generateDailyReports();
}
}
Memory sentence:
Scheduled job classes should trigger services, not contain all business logic.
20. Scheduling and Transactions
A scheduled method can call a transactional service.
Good:
@Component
public class TokenCleanupJob {
private final TokenCleanupService tokenCleanupService;
public TokenCleanupJob(TokenCleanupService tokenCleanupService) {
this.tokenCleanupService = tokenCleanupService;
}
@Scheduled(fixedDelay = 60_000)
public void cleanExpiredTokens() {
tokenCleanupService.cleanExpiredTokens();
}
}
Service:
@Service
public class TokenCleanupService {
private final TokenRepository tokenRepository;
public TokenCleanupService(TokenRepository tokenRepository) {
this.tokenRepository = tokenRepository;
}
@Transactional
public void cleanExpiredTokens() {
tokenRepository.deleteExpiredTokens(Instant.now());
}
}
Why good?
scheduled bean calls another Spring bean
transactional method is called through proxy
business logic is separated
Memory sentence:
Let scheduled jobs call transactional services.
21. Scheduling Self-Invocation Trap
Bad:
@Component
public class CleanupJob {
@Scheduled(fixedDelay = 60_000)
public void run() {
cleanExpiredTokens();
}
@Transactional
public void cleanExpiredTokens() {
// database work
}
}
Problem:
run() calls cleanExpiredTokens() inside same class
this can bypass transactional proxy behavior
Better:
put transactional logic in another service bean
Memory sentence:
Scheduling plus self-invocation can break proxy-based annotations.
22. What Is @Async?
@Async tells Spring:
Run this method asynchronously, usually in another thread.
Example:
@Service
public class EmailService {
@Async
public void sendWelcomeEmail(String email) {
// send email in background
}
}
Caller:
emailService.sendWelcomeEmail("user@example.com");
The caller can continue without waiting for the method to finish.
Memory sentence:
@Asyncruns a method in another thread.
23. Enable Async
To use @Async, enable async support:
@Configuration
@EnableAsync
public class AsyncConfig {
}
Or:
@SpringBootApplication
@EnableAsync
public class Application {
}
Memory sentence:
@EnableAsyncactivates async method processing.
24. Async Must Go Through Proxy
Like @Transactional, @Async is proxy-based.
Works:
Controller -> EmailService proxy -> async executor -> real method
Does not work:
EmailService.this.sendWelcomeEmail()
Example trap:
@Service
public class UserService {
public void registerUser() {
sendWelcomeEmail();
}
@Async
public void sendWelcomeEmail() {
// may not run async
}
}
This is self-invocation.
Memory sentence:
@Asyncneeds a call through the Spring proxy.
25. Async Return Types
Common async return types:
void
CompletableFuture<T>
Future<T>
Example:
@Async
public CompletableFuture<String> generateReport() {
String result = "report";
return CompletableFuture.completedFuture(result);
}
Caller:
CompletableFuture<String> future = reportService.generateReport();
Memory sentence:
Use
CompletableFuturewhen the caller needs an async result.
26. Async Void Exception Trap
Async void method:
@Async
public void sendEmail() {
throw new RuntimeException("Email failed");
}
Problem:
caller does not receive the exception directly
exception happens in another thread
must be logged/handled by async exception handling
Better for result/error handling:
@Async
public CompletableFuture<Void> sendEmail() {
try {
// send email
return CompletableFuture.completedFuture(null);
} catch (Exception exception) {
return CompletableFuture.failedFuture(exception);
}
}
Memory sentence:
Exceptions in async methods do not behave like normal synchronous exceptions.
27. What Is an Executor?
An executor runs tasks.
In Spring async, an executor decides:
which thread runs async method
how many threads can run
what happens when queue is full
thread names
rejection policy
Common class:
ThreadPoolTaskExecutor
Memory sentence:
An executor manages threads for async work.
28. Configure Async Executor
Example:
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "applicationTaskExecutor")
public Executor applicationTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("app-async-");
executor.initialize();
return executor;
}
}
Use it:
@Async("applicationTaskExecutor")
public void sendEmail(String email) {
}
Memory sentence:
Configure executors explicitly for production async work.
29. Thread Pool Concepts
| Concept | Meaning |
|---|---|
| Core pool size | normal number of threads |
| Max pool size | maximum allowed threads |
| Queue capacity | how many tasks can wait |
| Thread name prefix | helps logs/debugging |
| Rejection policy | what happens when full |
Memory sentence:
Thread pools need limits.
30. Thread Pool Trap
Bad mindset:
Async means unlimited background magic.
Reality:
threads are limited
CPU is limited
memory is limited
database connections are limited
external APIs are limited
queues can fill
If too many async tasks are submitted:
application can become slower
memory can grow
tasks can be rejected
database can be overloaded
external API can be overloaded
Memory sentence:
Async does not remove work; it moves work to another thread.
31. Async and Transactions
Important:
Async method runs in another thread.
Transaction context does not automatically continue into that thread.
Example:
@Transactional
public void registerUser() {
userRepository.save(user);
emailService.sendWelcomeEmailAsync(user.getEmail());
}
Async method:
@Async
public void sendWelcomeEmailAsync(String email) {
}
Problem:
async method may run before transaction commits
if transaction rolls back, email may still be sent
Better:
publish event
handle with @TransactionalEventListener(AFTER_COMMIT)
optionally add @Async
Memory sentence:
Async does not automatically wait for transaction commit.
32. Good Pattern: After Commit + Async
Publisher:
@Transactional
public UserDto register(RegisterUserRequest request) {
UserEntity user = userRepository.save(new UserEntity(request.email()));
eventPublisher.publishEvent(
new UserRegisteredEvent(user.getId(), user.getEmail())
);
return new UserDto(user.getId(), user.getEmail());
}
Listener:
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendWelcomeEmail(UserRegisteredEvent event) {
emailService.sendWelcomeEmail(event.email());
}
Meaning:
only after successful commit
then run email in another thread
Memory sentence:
For side effects after DB commit, combine transactional events and async carefully.
33. Async and Security Context
Async runs in another thread.
So thread-local context may not automatically be available.
Examples:
SecurityContext
MDC logging context
LocaleContext
transaction context
request context
Bad assumption:
The async method automatically sees the same request user.
Better:
pass needed values explicitly
userId
tenantId
email
correlationId
Memory sentence:
In async methods, pass required context explicitly.
34. Async and Request Scope
Do not rely on request-scoped objects inside async methods.
Bad:
@Async
public void doWork() {
requestScopedBean.getSomething();
}
Problem:
HTTP request may already be finished
request context may not exist in async thread
Better:
extract required data before async call
pass simple values into async method
Memory sentence:
Async work should not depend on the HTTP request still existing.
35. Scheduled Jobs in Multiple App Instances
Production often runs multiple app instances.
Example:
instance A
instance B
instance C
If all run the same scheduled job:
cleanup job runs three times
daily report sends three emails
billing job charges customers three times
This is dangerous.
Solutions:
distributed lock
leader election
external scheduler
database lock
ShedLock
Quartz cluster mode
Kubernetes CronJob
run job in only one worker instance
Memory sentence:
In multi-instance production, every instance may run the scheduled job.
36. Idempotency
Scheduled jobs should often be idempotent.
Idempotent means:
running it multiple times has the same final effect as running it once
Good cleanup job:
delete expired tokens
Usually safe if repeated.
Dangerous job:
charge customer
send invoice email
create monthly invoice
Needs protection.
Memory sentence:
Scheduled jobs should be safe to retry or protected from duplicates.
37. Long-Running Jobs
Long-running jobs need care.
Problems:
job overlaps with next run
transaction stays open too long
memory grows
database locks held too long
shutdown interrupts job
no progress tracking
no retry strategy
Better:
process in batches
commit per batch
store progress
use locking
monitor duration
set timeouts
make retry safe
Memory sentence:
Long jobs should be batched, monitored, and retry-safe.
38. Scheduled Job Example: Batch Cleanup
Bad:
@Scheduled(cron = "0 0 2 * * *")
@Transactional
public void deleteAllOldData() {
repository.deleteEverythingOlderThan(cutoff);
}
Potential problem:
huge transaction
locks many rows
slow rollback if failure
database pressure
Better:
@Scheduled(cron = "0 0 2 * * *")
public void cleanupOldData() {
int deleted;
do {
deleted = cleanupService.deleteNextBatch(500);
} while (deleted > 0);
}
Service:
@Transactional
public int deleteNextBatch(int size) {
return repository.deleteNextExpiredBatch(size);
}
Memory sentence:
For large jobs, prefer smaller transactional batches.
39. Monitoring Scheduled Jobs
Production scheduled jobs should be observable.
Track:
last successful run
last failure
duration
number of processed items
number of skipped items
number of errors
next expected run
Use:
logs
metrics
Actuator
alerts
database job history table
external monitoring
Memory sentence:
If a scheduled job matters, monitor it.
40. Actuator Scheduled Tasks
Spring Boot Actuator can expose scheduled task information when configured.
Useful endpoint:
/actuator/scheduledtasks
It can help inspect scheduled jobs.
Memory sentence:
Actuator can help observe scheduled tasks.
41. Testing Scheduled Logic
Do not wait for real time in tests.
Bad:
Thread.sleep(60_000);
Better:
move logic into service
unit test service directly
test scheduling annotation lightly if needed
integration test only important wiring
Example:
@Component
public class CleanupJob {
private final CleanupService cleanupService;
public CleanupJob(CleanupService cleanupService) {
this.cleanupService = cleanupService;
}
@Scheduled(fixedDelay = 60_000)
public void run() {
cleanupService.cleanup();
}
}
Unit test:
class CleanupServiceTest {
// test cleanup business logic directly
}
Memory sentence:
Test scheduled business logic as normal service logic.
42. Testing Async Logic
Do not rely on random sleeps.
Bad:
Thread.sleep(1000);
verify(emailService).sendEmail(...);
Better options:
test async method logic synchronously as normal method
mock executor for unit tests
use Awaitility for integration tests
return CompletableFuture and await result
Example:
CompletableFuture<String> future = reportService.generateReport();
assertThat(future.get()).isEqualTo("report");
Memory sentence:
Avoid sleep-based async tests.
43. Async Is Not Messaging
Important:
@Async is not Kafka.
@Async is not RabbitMQ.
@Async is not durable.
@Async does not survive app crash.
@Async does not guarantee retry.
Use message broker/outbox for:
durable delivery
cross-service communication
retry
replay
backpressure
long-running distributed workflows
Memory sentence:
Async is in-process background execution, not durable messaging.
44. Scheduling Is Not a Queue
Important:
@Scheduled is not a job queue.
It does not automatically provide:
durable job storage
retry history
distributed locking
cluster coordination
manual replay
advanced calendars
job dashboard
For advanced jobs, consider:
Quartz
Spring Batch
Kubernetes CronJob
message queue
workflow engine
external scheduler
Memory sentence:
@Scheduledis simple scheduling, not a full job platform.
45. When to Use @Scheduled
Good use cases:
simple cleanup
simple polling
periodic cache refresh
daily report trigger
health check task
small internal maintenance job
Be careful with:
billing
payments
critical emails
large batch jobs
distributed jobs
jobs requiring exactly-once execution
jobs needing retries and audit trail
Memory sentence:
Use
@Scheduledfor simple periodic jobs.
46. When to Use @Async
Good use cases:
non-critical background work
parallel independent work
email sending after commit
report generation
notification sending
external API call that should not block caller
Be careful with:
work that must be durable
work that must be retried
work that needs request/transaction context
unbounded workloads
security-sensitive context
Memory sentence:
Use
@Asyncfor local background work, not guaranteed delivery.
47. Common Exam Traps
Trap 1
@Scheduled needs scheduling support enabled.
Trap 2
@Async needs async support enabled.
Trap 3
Scheduled methods should not require arguments.
Trap 4
Return values from scheduled methods are ignored.
Trap 5
fixedRate measures start-to-start.
Trap 6
fixedDelay measures finish-to-next-start.
Trap 7
Cron is for calendar-based schedules.
Trap 8
Cron time zone matters.
Trap 9
@Async runs in another thread.
Trap 10
Async exceptions do not behave like normal synchronous exceptions.
Trap 11
@Async is proxy-based.
Trap 12
Self-invocation can bypass @Async.
Trap 13
Async does not automatically share transaction context.
Trap 14
Async does not automatically wait for commit.
Trap 15
Every app instance may run the same scheduled job.
Trap 16
Scheduled jobs should be idempotent or locked.
Trap 17
@Scheduled is not a distributed job system.
Trap 18
@Async is not durable messaging.
48. Real Exam Question: @Scheduled
Question:
What does @Scheduled do?
Answer:
@Scheduled marks a Spring bean method to be executed automatically according to a schedule, such as fixed rate, fixed delay, or cron.
49. Real Exam Question: Enable Scheduling
Question:
Which annotation enables scheduled method processing?
Answer:
@EnableScheduling.
50. Real Exam Question: Fixed Rate
Question:
What does fixed rate mean?
Answer:
Fixed rate runs executions at a regular interval measured from the start time of the previous execution.
51. Real Exam Question: Fixed Delay
Question:
What does fixed delay mean?
Answer:
Fixed delay waits a configured interval after the previous execution finishes before starting the next one.
52. Real Exam Question: Cron
Question:
When should I use cron?
Answer:
Use cron for calendar-based schedules, such as every day at 02:00 or every Monday at 08:00.
53. Real Exam Question: @Async
Question:
What does @Async do?
Answer:
@Async makes a Spring bean method execute asynchronously, usually in another thread through an executor.
54. Real Exam Question: Enable Async
Question:
Which annotation enables async method processing?
Answer:
@EnableAsync.
55. Real Exam Question: Executor
Question:
What is an executor?
Answer:
An executor manages threads and runs submitted tasks, including methods annotated with @Async.
56. Real Exam Question: Async Transaction
Question:
Does @Async automatically continue the caller’s transaction?
Answer:
No. Async methods run in another thread and do not automatically share the caller’s transaction context.
57. Interview Answer
Question:
Explain fixed rate vs fixed delay.
Good answer:
Fixed rate measures the interval from the start time of one execution to the start time of the next execution. Fixed delay waits until the previous execution finishes, then waits the configured delay before starting again. Fixed delay is often safer for jobs where I want to avoid pressure from slow executions.
58. Interview Answer
Question:
How do you use
@Asyncsafely?
Good answer:
I enable async support with @EnableAsync, configure a bounded executor with clear thread pool settings, and make sure the async method is called through a Spring proxy. I do not rely on transaction, request, or security context being automatically available. For side effects after database commit, I prefer publishing an event and handling it with @TransactionalEventListener(AFTER_COMMIT) plus @Async.
59. Interview Answer
Question:
What are production risks with
@Scheduled?
Good answer:
In production, scheduled jobs can accidentally run on every application instance, causing duplicate work. Long-running jobs can overlap, hold locks, or overload the database. Jobs need exception handling, monitoring, idempotency, and often distributed locking or an external scheduler. @Scheduled is good for simple jobs but not a full distributed job platform.
60. Interview Answer
Question:
Is
@Asyncthe same as messaging?
Good answer:
No. @Async only runs work in another thread inside the same application process. It is not durable, does not survive application crashes, and does not provide broker-level retry or replay. For durable cross-service communication, I would use a message broker such as Kafka or RabbitMQ, or an outbox pattern.
61. Tiny Code Practice
Create a cleanup job that runs every 10 minutes after the previous run finishes.
Possible answer:
@Component
public class CleanupJob {
private final CleanupService cleanupService;
public CleanupJob(CleanupService cleanupService) {
this.cleanupService = cleanupService;
}
@Scheduled(fixedDelay = 10, timeUnit = TimeUnit.MINUTES)
public void clean() {
cleanupService.cleanExpiredData();
}
}
Question:
Why use fixed delay?
Answer:
Because the next execution starts only after the previous one finishes and the delay passes.
62. Tiny Code Practice 2
Create an async email sender.
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "emailExecutor")
public Executor emailExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("email-");
executor.initialize();
return executor;
}
}
@Service
public class EmailService {
@Async("emailExecutor")
public void sendWelcomeEmail(String email) {
// send email
}
}
Memory sentence:
Named executors make async behavior explicit.
63. Tiny Bug Practice 1
Problem:
@Scheduled(fixedDelay = 5000)
public void run(String input) {
}
Question:
What is wrong?
Answer:
Scheduled methods should not require arguments because Spring calls them automatically.
64. Tiny Bug Practice 2
Problem:
@Service
public class UserService {
public void register() {
sendEmail();
}
@Async
public void sendEmail() {
}
}
Question:
Why might sendEmail() not run asynchronously?
Answer:
It is self-invocation. The method call does not go through the Spring proxy.
65. Tiny Bug Practice 3
Problem:
@Transactional
public void registerUser() {
userRepository.save(user);
emailService.sendWelcomeEmailAsync(user.getEmail());
throw new RuntimeException("rollback");
}
Question:
What is dangerous?
Answer:
The async email may be sent even though the transaction rolls back. Use an AFTER_COMMIT transactional event for this side effect.
66. Tiny Bug Practice 4
Problem:
App has 3 production instances.
Each instance has @Scheduled daily billing job.
Question:
What can go wrong?
Answer:
The billing job may run three times, possibly charging customers multiple times. Use distributed locking, leader election, external scheduler, or make the job idempotent.
Practice Questions and Answers
Question 1
What is scheduling?
Answer:
Scheduling means running a task automatically at a planned time or interval.
Question 2
What does @Scheduled do?
Answer:
@Scheduled marks a Spring bean method to run automatically according to fixed rate, fixed delay, or cron.
Question 3
What annotation enables scheduling?
Answer:
@EnableScheduling.
Question 4
What is fixed rate?
Answer:
Fixed rate means the interval is measured from the start time of one execution to the start time of the next.
Question 5
What is fixed delay?
Answer:
Fixed delay means Spring waits until the previous execution finishes, then waits the configured delay before starting again.
Question 6
What is cron used for?
Answer:
Cron is used for calendar-based schedules, such as every day at 02:00.
Question 7
Why does cron time zone matter?
Answer:
Because servers may run in UTC while the business schedule may need a specific time zone like Europe/Berlin, and daylight saving time may matter.
Question 8
What are rules for scheduled methods?
Answer:
Scheduled methods should usually have no arguments, return void or ignore return value, be on Spring-managed beans, and handle exceptions carefully.
Question 9
What does @Async do?
Answer:
@Async makes a Spring bean method run asynchronously, usually in another thread.
Question 10
What annotation enables async?
Answer:
@EnableAsync.
Question 11
What is an executor?
Answer:
An executor manages threads and runs submitted tasks.
Question 12
Why should thread pools be bounded?
Answer:
Because threads, memory, database connections, and external services are limited. Unbounded async work can overload the application.
Question 13
Does async continue the caller transaction automatically?
Answer:
No. Async methods run in another thread and do not automatically share the caller’s transaction context.
Question 14
Why can self-invocation break @Async?
Answer:
Because self-invocation calls the method on this, bypassing the Spring proxy that applies async behavior.
Question 15
Why should async methods not rely on request context?
Answer:
Because the HTTP request may already be finished and request context may not exist in the async thread.
Question 16
What problem happens with scheduled jobs in multiple app instances?
Answer:
Every instance may run the same job, causing duplicate work.
Question 17
What does idempotent mean?
Answer:
Idempotent means running the same operation multiple times has the same final effect as running it once.
Question 18
Why is @Async not durable messaging?
Answer:
Because @Async runs work inside the same application process and does not provide durable storage, retry, replay, or crash recovery.
Question 19
Why is @Scheduled not a full job platform?
Answer:
Because @Scheduled does not provide durable job storage, distributed locking, retry history, cluster coordination, or advanced job management by itself.
Question 20
How should I test scheduled and async logic?
Answer:
Move business logic into services and test those services directly. Avoid sleep-based tests. For async, return CompletableFuture or use proper waiting tools in integration tests.
Final Memory Sentences
- Scheduling is about time.
- Async is about threads.
@Scheduledruns methods automatically.@EnableSchedulingenables scheduling.fixedRatemeans start-to-start.fixedDelaymeans finish-to-next-start.- Cron is for calendar schedules.
- Cron time zone matters.
- Scheduled methods should not require arguments.
- Scheduled return values are ignored.
- Scheduled jobs should handle exceptions.
- Scheduled jobs should call service logic.
@Asyncruns methods in another thread.@EnableAsyncenables async.@Asyncis proxy-based.- Self-invocation can bypass
@Async. - Async does not automatically share transaction context.
- Async does not automatically wait for commit.
- Pass required context explicitly into async methods.
- Every production instance may run the same scheduled job.
- Scheduled jobs should be idempotent or locked.
@Scheduledis not a distributed job platform.@Asyncis not durable messaging.