Runnable vs.Callable în Java

Prezentare generală

de la primele zile ale Java, multithreading a fost un aspect major al limbii. Runnable este interfața de bază prevăzută pentru reprezentarea sarcinilor multi-threaded și Callable este o versiune îmbunătățită a Runnable care a fost adăugată în Java 1.5.

în acest articol, vom explora diferențele și aplicațiile ambelor interfețe.

mecanism de execuție

ambele interfețe sunt concepute pentru a reprezenta o sarcină care poate fi executată prin mai multe fire. SARCINI Runnable poate fi rulat folosind clasa Thread sau ExecutorService întrucât Callables poate fi rulat numai folosind acesta din urmă.

Return Values

să aruncăm o privire mai profundă asupra modului în care aceste interfețe gestionează valorile return.

3.1. Cu Runnable

interfața Runnable este o interfață funcțională și are o singură metodă run() care nu acceptă niciun parametru și nu returnează nicio valoare.

acest lucru este potrivit pentru situațiile în care nu căutăm un rezultat al execuției firului, de exemplu, înregistrarea evenimentelor primite:

public interface Runnable { public void run();}

să înțelegem acest lucru cu un exemplu:

public class EventLoggingTask implements Runnable{ private Logger logger = LoggerFactory.getLogger(EventLoggingTask.class); @Override public void run() { logger.info("Message"); }}

în acest exemplu, firul va citi doar un mesaj din coadă și îl va înregistra într-un fișier jurnal. Nu există nicio valoare returnată din sarcină; sarcina poate fi lansată folosind ExecutorService:

public void executeTask() { executorService = Executors.newSingleThreadExecutor(); Future future = executorService.submit(new EventLoggingTask()); executorService.shutdown();}

în acest caz, obiectul viitor nu va avea nicio valoare.

3.2. Cu Callable

interfața Callable este o interfață generică care conține o singură metodă call () – care returnează o valoare generică V:

public interface Callable<V> { V call() throws Exception;}

să aruncăm o privire la calcularea factorialului unui număr:

public class FactorialTask implements Callable<Integer> { int number; // standard constructors public Integer call() throws InvalidParamaterException { int fact = 1; // ... for(int count = number; count > 1; count--) { fact = fact * count; } return fact; }}

rezultatul metodei call () este returnat într-un obiect viitor:

@Testpublic void whenTaskSubmitted_ThenFutureResultObtained(){ FactorialTask task = new FactorialTask(5); Future<Integer> future = executorService.submit(task); assertEquals(120, future.get().intValue());}

manipularea excepțiilor

să vedem cât de potrivite sunt pentru manipularea excepțiilor.

4.1. Cu Runnable

deoarece semnătura metodei nu are clauza „aruncări” specificată, nu există nicio modalitate de a propaga excepții verificate în continuare.

4.2. Cu Call Call() Callable

Call () metoda Callable conține clauza” aruncă excepție”, astfel încât să putem propaga cu ușurință excepțiile verificate în continuare:

public class FactorialTask implements Callable<Integer> { // ... public Integer call() throws InvalidParamaterException { if(number < 0) { throw new InvalidParamaterException("Number should be positive"); } // ... }}

în cazul rulării unui apelabil folosind un ExecutorService, excepțiile sunt colectate în obiectul viitor, care poate fi verificat prin efectuarea unui apel către viitor.obține () metodă. Acest lucru va arunca o ExecutionException – care împachetează excepția inițială:

@Test(expected = ExecutionException.class)public void whenException_ThenCallableThrowsIt() { FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); Integer result = future.get().intValue();}

în testul de mai sus, ExecutionException este aruncat pe măsură ce trecem un număr nevalid. Putem apela getCause () metoda pe acest obiect excepție pentru a obține originalul verificat excepție.

dacă nu facem apelul la metoda get () a clasei viitoare – atunci excepția aruncată de metoda call () nu va fi raportată înapoi, iar sarcina va fi în continuare marcată ca finalizată:

@Testpublic void whenException_ThenCallableDoesntThrowsItIfGetIsNotCalled(){ FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); assertEquals(false, future.isDone());}

testul de mai sus va trece cu succes, chiar dacă am aruncat o excepție pentru valorile negative ale parametrului la FactorialCallableTask.

concluzie

în acest articol, am explorat diferențele dintre interfețele rulabile și apelabile.

ca întotdeauna, codul complet pentru acest articol este disponibil peste pe GitHub.

începeți cu Spring 5 și Spring Boot 2, prin cursul Learn Spring:

>> verificați cursul

Lasă un răspuns

Adresa ta de email nu va fi publicată.

Previous post Nitril
Next post Julian Morris pe ‘New Girl’ = cea mai bună idee vreodată