In Spring Boot, when you create two asynchronous threads to call a service and repository, they will run in an isolated manner. Each thread will have its own execution path and will not interfere with the other. This is due to the thread-safety feature of Springâs @Async
 annotation baeldung.com
For example, if you have two methods in a service class that call a repository and you annotate these methods with @Async
, they will run independently on separate threads dzone.com:
@Service
public class MyService {
@Autowired
private MyRepository myRepository;
@Async
public CompletableFuture<List<MyEntity>> method1() {
// This will run on a separate thread
return CompletableFuture.completedFuture(myRepository.findAll());
}
@Async
public CompletableFuture<MyEntity> method2(Long id) {
// This will also run on a separate thread
return CompletableFuture.completedFuture(myRepository.findById(id));
}
}
In the above code, method1
 and method2
 will run concurrently, each on a separate thread. This means that if method1
 takes a long time to execute, it will not block method2
 and vice versa.
However, you should be careful when working with mutable shared data in a multi-threaded environment. If both methods are modifying the same data, this could lead to data inconsistencies or race conditions. This is where thread-safety measures, such as synchronization, come into play.
Furthermore, calling a repository method (like save()
, delete()
, etc.) in an asynchronous method wonât affect the database operationâs atomicity. Each call to a repository method will open a new transaction (unless you manually manage transactions or use @Transactional
 to span a transaction across multiple methods), perform the operation, and then commit the transaction stackoverflow.com, stackoverflow.com.
When using @Autowired
to inject beans in Spring, the beans are not shared among threads unless they are designed to be thread-safe. If youâre using @Async
to create multiple threads, each thread will get its own copy of the bean, unless the bean is a singleton and stateful, which could lead to data inconsistency issues Source 0, Source 1, Source 4.
For example, consider the following service:
@Service
public class MyService {
@Autowired
private MyBean myBean;
@Async
public void asyncMethod1() {
myBean.setData("Data from method1");
}
@Async
public void asyncMethod2() {
myBean.setData("Data from method2");
}
}
In this case, asyncMethod1
and asyncMethod2
will be executed on separate threads. If MyBean
is a prototype bean (a new instance is created every time it is injected), then each method will get its own copy of MyBean
and there will be no data overwrite. However, if MyBean
is a singleton bean (the same instance is shared among all injection points), the data set in one method may be overwritten by the other method, leading to data inconsistency issues.
If youâre dealing with shared state in a multi-threaded environment, you should ensure that your beans are thread-safe. This can be achieved by making the bean stateless or by synchronizing access to shared resources. If the bean is stateful and not thread-safe, you should consider using a different scope, such as prototype
, request
, or session
, depending on your use case Source 0, Source 1, Source 4.
Keep in mind that @Async
methods run in a separate thread and may not have access to thread-bound resources such as HTTP request attributes or transactional contexts. If you need to access such resources, you should pass them as method parameters or consider using other mechanisms such as ThreadLocal
Source 0.