áttekintés
mivel az első napokban a Java, többszálú volt a fő szempont a nyelv. A Runnable a többszálú feladatok ábrázolására szolgáló alapvető felület, a Callable pedig a Runnable továbbfejlesztett változata, amelyet a Java 1.5-ben adtak hozzá.
ebben a cikkben megvizsgáljuk a két interfész különbségeit és alkalmazásait.
végrehajtási mechanizmus
mindkét interfész úgy van kialakítva, hogy egy több szál által végrehajtható feladatot ábrázoljon. A futtatható feladatok futtathatók a Thread osztály vagy az ExecutorService használatával, míg a Callables csak az utóbbi használatával futtatható.
visszatérési értékek
vessünk egy mélyebb pillantást arra, hogy ezek az interfészek hogyan kezelik a visszatérési értékeket.
3.1. Runnable
esetén a Runnable interfész egy funkcionális interfész, és egyetlen run () metódussal rendelkezik, amely nem fogad el semmilyen paramétert és nem ad vissza semmilyen értéket.
ez alkalmas olyan helyzetekre, amikor nem a szál végrehajtásának eredményét keressük, például bejövő események naplózása:
public interface Runnable { public void run();}
értsük meg ezt egy példával:
public class EventLoggingTask implements Runnable{ private Logger logger = LoggerFactory.getLogger(EventLoggingTask.class); @Override public void run() { logger.info("Message"); }}
ebben a példában a szál csak kiolvas egy üzenetet a sorból, majd naplófájlba rögzíti. A feladat nem ad vissza értéket; a feladat elindítható az ExecutorService használatával:
public void executeTask() { executorService = Executors.newSingleThreadExecutor(); Future future = executorService.submit(new EventLoggingTask()); executorService.shutdown();}
ebben az esetben a jövőbeli objektumnak nincs értéke.
3.2. Val vel hívható
A hívható felület egy általános felület, amely egyetlen hívást () metódust tartalmaz-amely általános értéket ad vissza V:
public interface Callable<V> { V call() throws Exception;}
vessünk egy pillantást egy szám faktoriálisának kiszámítására:
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; }}
a call() metódus eredménye egy jövőbeli objektumon belül kerül visszaadásra:
@Testpublic void whenTaskSubmitted_ThenFutureResultObtained(){ FactorialTask task = new FactorialTask(5); Future<Integer> future = executorService.submit(task); assertEquals(120, future.get().intValue());}
kivételkezelés
lássuk, mennyire alkalmasak a kivételek kezelésére.
4.1. Futtatható
mivel a metódus aláírása nem rendelkezik a” dobások ” záradékkal, nincs mód további ellenőrzött kivételek terjesztésére.
4.2. A Callable
Callable ‘s call () metódus” dob kivétel ” záradékot tartalmaz, így könnyen tovább tudjuk terjeszteni az ellenőrzött kivételeket:
public class FactorialTask implements Callable<Integer> { // ... public Integer call() throws InvalidParamaterException { if(number < 0) { throw new InvalidParamaterException("Number should be positive"); } // ... }}
abban az esetben fut egy hívható egy ExecutorService, a kivételek gyűjtik a jövőben objektum, amely ellenőrizhető, hogy egy hívást a jövőben.get () módszer. Ez dob egy ExecutionException – amely becsomagolja az eredeti kivételt:
@Test(expected = ExecutionException.class)public void whenException_ThenCallableThrowsIt() { FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); Integer result = future.get().intValue();}
a fenti tesztben az ExecutionException dobásra kerül, mivel érvénytelen számot adunk át. Hívhatjuk a getcause () metódust ezen a kivétel objektumon, hogy megkapjuk az eredeti ellenőrzött kivételt.
ha nem kezdeményezzük a hívást a Get () metódusra a Future class-ban, akkor a call () metódus által dobott kivétel nem kerül jelentésre, és a feladat továbbra is befejezettként lesz megjelölve:
@Testpublic void whenException_ThenCallableDoesntThrowsItIfGetIsNotCalled(){ FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); assertEquals(false, future.isDone());}
a fenti teszt sikeres lesz, annak ellenére, hogy kivételt dobtunk a paraméter negatív értékeire a FactorialCallableTask számára.
következtetés
ebben a cikkben a futtatható és a hívható interfészek közötti különbségeket tártuk fel.
mint mindig, a cikk teljes kódja elérhető a Githubon.
kezdje el a Spring 5 és a Spring Boot 2 használatával a Learn Spring tanfolyamon keresztül:
>> nézze meg a tanfolyamot