4


I am developing an API. This API needs to do 2 DB queries to get the result.
I tried following strategies:

  • Used callable as return type in Controller.
  • Created 2 threads in Service (use Callable and CoundownLatch) to run 2 queries parallel and detect finishing time.

    public class PetService {
        public Object getData() {
            CountDownLatch latch = new CountDownLatch(2);
            AsyncQueryDBTask<Integer> firstQuery= new AsyncQueryDBTask<>(latch);
            AsyncQueryDBTask<Integer> secondQuery= new AsyncQueryDBTask<>(latch);
            latch.await();
    }
    
    public class AsyncQueryDBTask<T> implements Callable {
    
       private CountDownLatch latch;
    
       public AsyncQueryDBTask(CountDownLatch latch) { this.latch = latch;}
    
       @Override
       public T call() throws Exception {
        //Run query
        latch.countDown();
       }
    

It worked fine but I feel that I am breaking the structure of Spring somewhere.

I wonder what is the most efficient way to get data in Spring 4.
-How to know both of 2 threads that run own query completed their job?
-How to control thread resource such as use and release thread?

Thanks in advance.

duy
  • 579
  • 5
  • 16

1 Answers1

8

You generally don't want to create your own threads in an ApplicationServer nor manage thread lifecycles. In application servers, you can submit tasks to an ExecutorService to pool background worker threads.

Conveniently, Spring has the @Async annotation that handles all of that for you. In your example, you would create 2 async methods that return a Future :

public class PetService {
    public Object getData() {
        Future<Integer> futureFirstResult = runFirstQuery();
        Future<Integer> futureSecondResult = runSecondQuery();

        Integer firstResult = futureFirstResult.get();
        Integer secondResult = futureSecondResult.get();
    }

    @Async
    public Future<Integer> runFirstQuery() {
        //do query
        return new AsyncResult<>(result);
    }

    @Async
    public Future<Integer> runSecondQuery() {
        //do query
        return new AsyncResult<>(result);
    }
}

As long as you configure a ThreadPoolTaskExecutor and enable async methods, Spring will handle submitting the tasks for you.

NOTE: The get() method blocks the current thread until a result is returned by the worker thread but doesn't block other worker threads. It's generally advisable to put a timeout to prevent blocking forever.

Laplie Anderson
  • 6,345
  • 4
  • 33
  • 37
  • Thanks for your answer. It is really helpful. Please help me one more question. Is it ok to do it: ```java List l = new ArrayList(); l.add(future1.get()); l.add(future2.get()); ``` – duy Feb 01 '17 at 06:20
  • Yes that's fine. The get call returns the value in the future always (or throws an exception if you use the timeout version) – Laplie Anderson Feb 01 '17 at 06:34
  • 1
    I guess in your example it will not work, because you call async method from class directly, self-invocation doesn't work because it bypasses the proxy and calls the underlying method directly. – Denis Aug 11 '21 at 07:33