Waarom start je geen discussie in de constructeur?

1. Overzicht

In deze korte tutorial gaan we zien waarom we geen thread in een constructor moeten starten.

Eerst introduceren we kort het publicatieconcept in Java en JVM. Vervolgens zullen we zien hoe dit concept van invloed is op de manier waarop we discussies starten.

2. Publicatie en ontsnapping

Elke keer dat we een object beschikbaar maken voor een andere code buiten het huidige bereik, publiceren we in feite dat object. Publiceren gebeurt bijvoorbeeld wanneer we een object retourneren, opslaan in een openbaar referentie, of zelfs doorgeven aan een andere methode.

Wanneer we een object publiceren dat we niet zouden moeten hebben, zeggen we dat het object is ontsnapt.

Er zijn veel manieren waarop we een objectreferentie kunnen laten ontsnappen, zoals het publiceren van het object voordat het volledig is geconstrueerd. Dit is in feite een van de meest voorkomende vormen van ontsnapping: wanneer de dit referentie ontsnapt tijdens de constructie van een object.

Wanneer de dit referentie ontsnapt tijdens de constructie, andere threads kunnen dat object in een onjuiste en niet volledig geconstrueerde staat zien. Dit kan op zijn beurt vreemde complicaties met betrekking tot de draadveiligheid veroorzaken.

3. Ontsnappen met draden

Een van de meest voorkomende manieren om de dit reference escape is om een ‚Äč‚Äčthread in een constructor te starten. Laten we een voorbeeld bekijken om dit beter te begrijpen:

openbare klasse LoggerRunnable implementeert Runnable {openbare LoggerRunnable () {Thread thread = nieuwe Thread (dit); // dit ontsnapt thread.start (); } @Override public void run () {System.out.println ("Gestart ..."); }}

Hier passeren we expliciet de dit verwijzing naar de Draad constructeur. Daarom de nieuw gestarte thread kan het omsluitende object mogelijk zien voordat de volledige constructie is voltooid. In gelijktijdige contexten kan dit subtiele bugs veroorzaken.

Het is ook mogelijk om het dit verwijzing impliciet:

openbare klasse ImplicitEscape {openbare ImplicitEscape () {Thread t = nieuwe Thread () {@Override public void run () {System.out.println ("Gestart ..."); }}; t.start (); }}

Zoals hierboven weergegeven, maken we een anonieme innerlijke klasse die is afgeleid van de Draad. Omdat innerlijke klassen een verwijzing naar hun omsluitende klasse behouden, is de dit verwijzing ontsnapt opnieuw uit de constructor.

Er is niets inherent mis met het maken van een Draad binnen een constructor. Het wordt echter ten zeerste afgeraden om het onmiddellijk te starten, zoals meestal, eindigen we met een ontsnapte dit verwijzing, expliciet of impliciet.

3.1. Alternatieven

In plaats van een thread in een constructor te starten, kunnen we een speciale methode voor dit scenario declareren:

openbare klasse SafePublication implementeert Runnable {privé laatste thread; openbare SafePublication () {thread = nieuwe thread (dit); } @Override public void run () {System.out.println ("Gestart ..."); } public void start () {thread.start (); }} ;:

Zoals hierboven getoond, publiceren we nog steeds het dit verwijzing naar de Draad. Deze keer starten we de thread nadat de constructor retourneert:

SafePublication-publicatie = nieuwe SafePublication (); publicatie.start ();

Daarom ontsnapt de objectreferentie niet naar een andere thread voordat deze volledig is opgebouwd.

4. Conclusie

In deze korte tutorial zagen we, na een korte introductie van de veilige publicatie, waarom we geen thread binnen een constructor zouden moeten beginnen.

Meer gedetailleerde informatie over de publicatie en escape in Java is te vinden in het Java Concurrency in Practice-boek.

Zoals gewoonlijk zijn alle voorbeelden beschikbaar op GitHub.