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.