Skip to main content

Week 8 Day 3 — Scheduling and Async

Goal

Today I want to understand scheduling and asynchronous execution in Spring.

Main questions:

  1. What is scheduling?
  2. What is @Scheduled?
  3. What is @EnableScheduling?
  4. What is fixed rate?
  5. What is fixed delay?
  6. What is cron?
  7. What is @Async?
  8. What is @EnableAsync?
  9. What is an executor?
  10. What is a thread pool?
  11. What does async actually mean?
  12. What are common production traps?
  13. 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.
  • ApplicationEventPublisher publishes events.
  • @EventListener receives events.
  • Events are synchronous by default.
  • @TransactionalEventListener(AFTER_COMMIT) runs after successful commit.
  • @Async can 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

TopicSchedulingAsync
Main questionWhen should the task run?Which thread should run the method?
Annotation@Scheduled@Async
Enable with@EnableScheduling@EnableAsync
Common useperiodic jobsbackground execution
Exampleclean expired tokens every hoursend 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:

@Scheduled makes 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:

@EnableScheduling activates 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

TopicfixedRatefixedDelay
Measures fromstart timefinish time
Meaningrun every X mswait X ms after completion
Best forregular polling rhythmavoid overlap / give rest after work
Riskoverlap or pressure if job is slowschedule 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:

initialDelay delays 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:

@Scheduled works 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:

@Async runs 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:

@EnableAsync activates 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:

@Async needs 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 CompletableFuture when 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

ConceptMeaning
Core pool sizenormal number of threads
Max pool sizemaximum allowed threads
Queue capacityhow many tasks can wait
Thread name prefixhelps logs/debugging
Rejection policywhat 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:

@Scheduled is 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 @Scheduled for 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 @Async for 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 @Async safely?

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 @Async the 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.
  • @Scheduled runs methods automatically.
  • @EnableScheduling enables scheduling.
  • fixedRate means start-to-start.
  • fixedDelay means 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.
  • @Async runs methods in another thread.
  • @EnableAsync enables async.
  • @Async is 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.
  • @Scheduled is not a distributed job platform.
  • @Async is not durable messaging.