Apeluri asincrone realizate în Java
Posted by Dumitru on Oct 10, 2009
Limbajul Java nu oferă implicit tehnici pentru apeluri asincrone de metode, dar ne sunt oferite tehnologii cum ar fi JMS (Java Message Service) sau Web Services. De sigur ambele sunt soluţii tehnologice foarte bune, dar este oare necesar utilizarea unor intregi sisteme pentru un “banal” apel asincron de metodă. Răspunsul nu este unul simplu, căci depinde foarte mult ce realizează metoda apelată.
În ceea ce urmează aş dori să prezint o soluţie care ar putea să fie o alternativă în cazul în care se doreşte evitarea utilizării unor arhitecturi prea “grandioase”. Structura acesteia cuprinde elementele prezentate în diagrama de mai jos.

Entităţile din structura prezentată în cazul unui apel asincron implică următoarele activităţi:
- clientul creează un obiect de tip MethodRequest (instanţa clasei Print) ce implementează metoda Do (executată concurent);
- clientul apelează metoda BeginInvoke al clasei AsyncCall, transmiţîndu-i obiectul de tip MethodRequest, dar şi primind un obiect MethodResult care ne va “identifica” apelul;
- AsyncCall prin metoda sa BeginInvoke va crea firul ce în momentul execuţiei va apela metoda Do al obiectului de tip MethodRequest;
- la finele execuţiei metodei Do rezultatul va fi “salvat” în obiectul de tip MethodResult;
- clientul va putea afla rezultatul execuţiei prin apelul metodei EndInvoke al clasei AsyncCall transmiţind “identificatorul” MethodResult;

Pentru exemplificare fie clasa client AsyncCallsMain în care vor fi prezente două apeluri asincrone (asincronismul este atins prin execuţia concurentă a metodei “încapsulată” într-un obiect Print de tip MethodRequest).
public class AsyncCallsMain { public static void main(String[] args) { MethodResult mResult1 = AsyncCall.BeginInvoke(new Print()); MethodResult mResult2 = AsyncCall.BeginInvoke(new Print()); String str1 = (String) AsyncCall.EndInvoke(mResult1); String str2 = (String) AsyncCall.EndInvoke(mResult2); System.out.println("Main method "); System.out.println(str1); System.out.println(str1); } }
Clasa Print realizând interfaţa MethodRequest, poate fi transmisă ca parametru firului “servitor” ce va executa concurent metoda Do:
class Print implements MethodRequest{ static Integer c = new Integer(0); @Override public Object Do(){ String strReturnObject = "Print: " + (++c).toString() + "\n"; // System.out.println(strReturnObject); return strReturnObject; } }
Clasa AsyncCall conţine metodele ce ne asigură apelurile asincrone. Metodele sunt statice, în perspectiva creşterii performanţei. Iar crearea şi lansarea firului de execuţie “servitor” introduce asincronismul necesar în invocarea şi execuţia metodei încapsulate în Print.
package Concurrency.Asynchronous; public class AsyncCall{ public static MethodResult BeginInvoke(MethodRequest mRequest){ final MethodRequest methodRequest = mRequest; final MethodResult mResult = new MethodResult(); Thread servant; (servant = new Thread(new Runnable() { public void run() { mResult.result = methodRequest.Do(); }})).start(); mResult.servant = servant; return mResult; } public static Object EndInvoke(MethodResult mResult) { try{ mResult.servant.join(); }catch(Exception ex){ //... } return mResult.result!=null?mResult.result:null; } }
Interfaţa MethodRequest defineşte metoda publică Do, care returnează un obiect “nedefinit” (rezultatul execuţiei metodei implementate în Print). Deci clientul va trebui să realizeze conversia necesară, precum este prezentat în clasa AsyncCallsMain, pentru a putea utiliza valoarea corectă.
package Concurrency.Asynchronous; public interface MethodRequest{ public Object Do(); }
Clasa MethodResult este utilizată ca şi un jeton (token) care identifică apelul. Aici vom păstra referinţa spre firul care serveşte metoda. Şi deoarece rezultatul este primit asincron îl vom păstra şi pe el în acest obiect.
package Concurrency.Asynchronous; public class MethodResult { Object result; Thread servant; }
În final aş menţiona că această soluţie nicidecum nu este o soluţie completă sau perfectă. Ea poate fi dezvoltată astfel încît metoda invocată să poată avea şi parametri. În această ordine de idei o sugestie ar putea fi clasa lui Rakhitha Karunarathne ce utilizează tehnici Reflection.