Uitvoerbaar versus opvraagbaar in Java

1. Overzicht

Sinds de begindagen van Java is multithreading een belangrijk aspect van de taal. Runnable is de kerninterface voor het weergeven van multi-threaded taken en Oproepbaar is een verbeterde versie van Runnable dat is toegevoegd in Java 1.5.

In dit artikel zullen we de verschillen en de toepassingen van beide interfaces onderzoeken.

2. Uitvoeringsmechanisme

Beide interfaces zijn ontworpen om een ​​taak weer te geven die kan worden uitgevoerd door meerdere threads. Runnable taken kunnen worden uitgevoerd met de Draad klasse of ExecutorService terwijl Oproepen kan alleen worden uitgevoerd met behulp van de laatste.

3. Retourwaarden

Laten we eens nader kijken naar de manier waarop deze interfaces omgaan met retourwaarden.

3.1. Met Runnable

De Runnable interface is een functionele interface en heeft een enkele rennen() methode die geen parameters accepteert en geen waarden retourneert.

Dit is geschikt voor situaties waarin we niet op zoek zijn naar een resultaat van de uitvoering van de thread, bijvoorbeeld logboekregistratie van inkomende gebeurtenissen:

openbare interface Runnable {public void run (); }

Laten we dit begrijpen met een voorbeeld:

openbare klasse EventLoggingTask implementeert Runnable {private Logger-logger = LoggerFactory.getLogger (EventLoggingTask.class); @Override public void run () {logger.info ("Bericht"); }}

In dit voorbeeld leest de thread gewoon een bericht uit de wachtrij en logt het in een logbestand in. Er wordt geen waarde geretourneerd van de taak; de taak kan worden gestart met UitvoerderService:

openbare void executeTask () {executorService = Executors.newSingleThreadExecutor (); Toekomstige toekomst = executorService.submit (nieuwe EventLoggingTask ()); executorService.shutdown (); }

In dit geval is het Toekomst object zal geen enkele waarde hebben.

3.2. Met Oproepbaar

De Oproepbaar interface is een generieke interface die een enkele bellen () methode - die een generieke waarde retourneert V.:

openbare interface Oproepbare {V call () gooit uitzondering; }

Laten we eens kijken naar het berekenen van de faculteit van een getal:

openbare klasse FactorialTask ​​implementeert Callable {int number; // standard constructors public Integer call () gooit InvalidParamaterException {int fact = 1; // ... for (int count = number; count> 1; count--) {fact = fact * count; } terugkeer feit; }}

Het resultaat van bellen () methode wordt geretourneerd binnen een Toekomst voorwerp:

@Test openbare ongeldigheid whenTaskSubmitted_ThenFutureResultObtained () {FactorialTask-taak = nieuwe FactorialTask ​​(5); Future future = executorService.submit (taak); assertEquals (120, future.get (). intValue ()); }

4. Afhandeling van uitzonderingen

Laten we eens kijken hoe geschikt ze zijn voor het afhandelen van uitzonderingen.

4.1. Met Runnable

Aangezien de handtekening van de methode niet de gespecificeerde "throws" -clausule heeft,er is geen manier om verder gecontroleerde uitzonderingen door te geven.

4.2. Met Oproepbaar

Oproep oproep () methode bevat “worpen Uitzondering" clausule zodat we gecontroleerde uitzonderingen gemakkelijk verder kunnen verspreiden:

public class FactorialTask ​​implementeert Callable {// ... public Integer call () gooit InvalidParamaterException {if (number <0) {throw new InvalidParamaterException ("Number moet positief zijn"); } // ...}}

In het geval dat u een Oproepbaar met een ExecutorService, de uitzonderingen worden verzameld in de Toekomst object, dat kan worden gecontroleerd door het Future.get () methode. Dit zal een ExecutionException - die de oorspronkelijke uitzondering omhult:

@Test (verwacht = ExecutionException.class) public void whenException_ThenCallableThrowsIt () {FactorialCallableTask-taak = nieuwe FactorialCallableTask (-5); Future future = executorService.submit (taak); Integer resultaat = future.get (). IntValue (); }

In de bovenstaande test is het ExecutionException wordt gegooid terwijl we een ongeldig nummer doorgeven. We kunnen de getCause () methode op dit uitzonderingsobject om de oorspronkelijke gecontroleerde uitzondering op te halen.

Als we niet bellen naar het krijgen() methode van Toekomst class - dan is de uitzondering die door wordt gegooid bellen () methode wordt niet teruggemeld en de taak wordt nog steeds als voltooid gemarkeerd:

@Test openbare leegte whenException_ThenCallableDoesntThrowsItIfGetIsNotCalled () {FactorialCallableTask-taak = nieuwe FactorialCallableTask (-5); Future future = executorService.submit (taak); assertEquals (false, future.isDone ()); }

De bovenstaande test zal met succes slagen, ook al hebben we een uitzondering gegenereerd voor de negatieve waarden van de parameter to FactorialCallableTask.

5. Conclusie

In dit artikel hebben we de verschillen tussen de Runnable en Oproepbaar interfaces.

Zoals altijd is de volledige code voor dit artikel beschikbaar op GitHub.