översikt
sedan början av Java har multitrådning varit en viktig aspekt av språket. Runnable är kärngränssnittet för att representera flertrådade uppgifter och Callable är en förbättrad version av Runnable som lades till i Java 1.5.
i den här artikeln kommer vi att undersöka skillnaderna och applikationerna för båda gränssnitten.
Exekveringsmekanism
båda gränssnitten är utformade för att representera en uppgift som kan utföras av flera trådar. Körbara uppgifter kan köras med Trådklassen eller ExecutorService medan Callables endast kan köras med den senare.
returvärden
Låt oss ta en djupare titt på hur dessa gränssnitt hanterar returvärden.
3.1. Med Runnable
är Runnable-gränssnittet ett funktionellt gränssnitt och har en enda run () – metod som inte accepterar några parametrar och returnerar inga värden.
detta är lämpligt för situationer där vi inte letar efter ett resultat av trådkörningen, till exempel inkommande händelseloggning:
public interface Runnable { public void run();}
låt oss förstå detta med ett exempel:
public class EventLoggingTask implements Runnable{ private Logger logger = LoggerFactory.getLogger(EventLoggingTask.class); @Override public void run() { logger.info("Message"); }}
i det här exemplet läser tråden bara ett meddelande från kön och loggar det i en loggfil. Det finns inget värde som returneras från uppgiften; uppgiften kan startas med ExecutorService:
public void executeTask() { executorService = Executors.newSingleThreadExecutor(); Future future = executorService.submit(new EventLoggingTask()); executorService.shutdown();}
i det här fallet kommer det framtida objektet inte att ha något värde.
3.2. Med Callable
är Callable-gränssnittet ett generiskt gränssnitt som innehåller en enda call () – metod-som returnerar ett generiskt värde V:
public interface Callable<V> { V call() throws Exception;}
Låt oss ta en titt på att beräkna faktorn för ett tal:
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; }}
resultatet av anropsmetoden () returneras inom ett framtida objekt:
@Testpublic void whenTaskSubmitted_ThenFutureResultObtained(){ FactorialTask task = new FactorialTask(5); Future<Integer> future = executorService.submit(task); assertEquals(120, future.get().intValue());}
Undantagshantering
Låt oss se hur lämpliga de är för undantagshantering.
4.1. Med Runnable
eftersom metodsignaturen inte har ”kast” – klausulen specificerad, finns det inget sätt att sprida ytterligare kontrollerade undantag.
4.2. Med Callable
Callable ’ s call () – metoden innehåller ”throws Exception” – klausul så att vi enkelt kan sprida kontrollerade undantag ytterligare:
public class FactorialTask implements Callable<Integer> { // ... public Integer call() throws InvalidParamaterException { if(number < 0) { throw new InvalidParamaterException("Number should be positive"); } // ... }}
om du kör en Callable med en ExecutorService samlas undantagen i det framtida objektet, vilket kan kontrolleras genom att ringa till framtiden.skaffa sig () metod. Detta kommer att kasta en ExecutionException-som sveper det ursprungliga undantaget:
@Test(expected = ExecutionException.class)public void whenException_ThenCallableThrowsIt() { FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); Integer result = future.get().intValue();}
i ovanstående test, ExecutionException kastas när vi passerar ett ogiltigt nummer. Vi kan ringa getCause () – metoden på detta undantagsobjekt för att få det ursprungliga kontrollerade undantaget.
om vi inte ringer till get () – metoden för framtida klass-kommer undantaget som kastas av call () – metoden inte att rapporteras tillbaka, och uppgiften kommer fortfarande att markeras som slutförd:
@Testpublic void whenException_ThenCallableDoesntThrowsItIfGetIsNotCalled(){ FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); assertEquals(false, future.isDone());}
ovanstående test kommer att passera framgångsrikt även om vi har kastat ett undantag för de negativa värdena för parametern till FactorialCallableTask.
slutsats
i den här artikeln har vi undersökt skillnaderna mellan de körbara och Anropbara gränssnitten.
som alltid är den fullständiga koden för den här artikeln tillgänglig över på GitHub.
Kom igång med Spring 5 och Spring Boot 2, genom Learn Spring course:
>> kolla in kursen