javaasynchronoussingle-threaded

What makes asynchronous single threaded java code asynchronous


So we are currently working on an app in java and a colleague introduced me to asynchronous programming and it doesn't matter how much I research it, to me it seems that is synchronous. So the main advantage of asynchronous code appears to be the non freezing of the UI while some action takes long time to be performed and get us the result back, all we need to do is give that function a callback function which is going to be called when the job is done. From what I have understood from online sources and similar questions on stackoverflow is that the code can be single threaded and async, someone even drew it like this:

Asynchronous (one thread)(credit: Asynchronous vs synchronous execution, what does it really mean?):

     A-Start ------------------------------------------ A-End   
       | B-Start -----------------------------------------|--- B-End   
       |    |      C-Start ------------------- C-End      |      |   
       |    |       |                           |         |      |
       V    V       V                           V         V      V      
1 thread->|<-A-|<--B---|<-C-|-A-|-C-|--A--|-B-|--C-->|---A---->|--B-->| 

so from what I understand the single threaded asynchronous code is still synchronous but it gives every task a little bit of time and if there is much to do the async code basically says call me back when the job is done, ok but if we only have one thread who is going to do the job while you give the resources to the other code?

to show you the Problem lets look at the example from our project:

 public void getAllExercises(RequestListener<List<Exercise>> listener) {
        getAllExercisesRequest().getAsObjectList(Exercise.class, listener);
    }

usage of that method: when the user clicks a button to see all exercises, this method is called and what it does is that it gets all exercises from server, here is an example of usage:

class sometingAdapter extends something{

someMethod(param){
         .
         . 
         .  
    //some code before the Method call 
    DBClient.getstandardDBClient().getAllExercises(new RequestListener<List<Exercise>>() {
                @Override
                public void onResponse(List<Exercise> response) {
                    arrayAdapter.addAll(response);
                    arrayAdapter.notifyDataSetChanged();
                }
            });
    
            //some additional code 
            return v;
        }-> end of the method

}

The claim is that now while the Method is fetching the data from server, our UI is not blocked, and other actions can be performed, but how ? when the code execution comes to the line DBClient.getStandardDBClient().getAllExercises then the method getAllExercises gets called the execution jumps into the method. Inside that method a Request is built and getAsObject(MyClass.class, listener) method is called (I dont know how getAsObject() method works, its from FAN API),

but where in all of this do we or the system tells the code, listen you can go back and do your stuff and when I have the data (the response) I will call you back with the onResponse() method from the listener that you passed as argument

and who does the job of fetching the data we only have one thread, there are no magical resources that fetch the data while our thread is busy executing other code? and how does the system know where to jump back while the other code is running? its not like system is like "oh he's doing it async I will magically jump around"

so after this magic happens there is one more stuff that confuses me, while the call to getAllExercises is still active when the further execution of the code reaches the return v statement isn't it so that the function call gets removed from stack, which would further mean that the execution of getAllExercise method would be interrupted ? which further means that the fetching would also be interrupted etc. basically it would cause a chain reaction and the onResponse() method would never be called or would it ?

What does make the code async, is it only passing a callback method i.e object whose method is used as callback? if so, how is that not blocking the UI, if I call a method and pass this method another method then the execution of the code will be inside of the Method, until the method that is passed is called, then the execution of that code will start so there is still a blockage of UI there is no magical jumping around and doing work in the background, what am I missing ?

                           void Method1(Method2);

                                    |Start 
                                    |call Method1------------>|
                                    |                         |                         
                                    | Blocked??               |Execution of Method 1
                                    |                         |
                                    | <-----------------------|
                                    |    callback Method2
                                    | continued execution 
                                    | 
                                    |
                                    |End 

So where does the magic happen where the blocked part becomes unblocked? and the execution of method1 and the rest are happening simultaneously?

My understanding is that somehow following happens:

                              void Method1(Method2);

                                    |Start 
                                    |call Method1------------------>|
  continues execution of main thread|<------------------------------|                         
                                    |(okay thanks for the Method    |Execution of Method 1
                                    |I will call you when I am done)|but what resource is executing 
                                    | <---------------------------- |Method 1????
                                    |    callback Method2: Iam done 
                                    | ------------------------------>|execution of method 2 
                                    |  Blocked                       |very quick not noticable 
                                    |<-------------------------------
                                    |continued execution
                                    |End


                                       

Solution

  • Generally in Java, if you do not want to have your UI blocking, you will have to run complex tasks on another thread. Without knowing this "FAN API" I can almost certainly say, that getAsObjectList(...) is starting this second thread.

    This method will return without noticeable delay, and allow the current thread to proceed, i. e. prevent the UI from blocking. Instead of initiating the request synchronously, it starts a new thread (or rather takes a thread from a thread pool) and uses it to send and await the request. When receiving the response it will call your RequestListener from this thread (or possibly yet another). That is to say, it will not use the thread, that called getAsObjectList(...) to run the RequestListener, since that thread has returned from getAsObjectList(...) a long time ago and cannot be "controlled" anymore.¹ Particularly there will not be some "magic" freely interrupting the thread at any random point of continued execution and forcefully stuffing the execution of the RequestListener into its control flow.


    ¹: If getAllExercises(...) was called from some thread, that is part of a thread pool/some event queue based concurrency framework (possibly part of the UI framework), it in fact is possible, that this thread will intentionally (or even just accidentally) be reused to call the RequestListener. See, for example JavaScript's concurrency model, where all the (client) code is only ever run in a single thread, while still allowing/encouraging/enforcing asynchronicity. However, this will still not happen, before your client code around the call of getAsObjectList(...) was smoothly executed. Only after that execution finished, and the thread has returned to some resting state in its pool, waiting for some more tasks to run, it can be selected as the thread to now call the RequestListener on.