Hvorfor ikke starte en tråd i konstruktøren?

1. Oversikt

I denne raske opplæringen skal vi se hvorfor vi ikke skal starte en tråd inne i en konstruktør.

Først vil vi kort introdusere publikasjonskonseptet i Java og JVM. Så får vi se hvordan dette konseptet påvirker måten vi starter tråder på.

2. Publisering og flukt

Hver gang vi gjør et objekt tilgjengelig for en annen kode utenfor det nåværende omfanget, publiserer vi i utgangspunktet det objektet. For eksempel skjer publisering når vi returnerer et objekt, lagrer det i en offentlig referanse, eller til og med gi den til en annen metode.

Når vi publiserer et objekt som vi ikke burde ha, sier vi at objektet har rømt.

Det er mange måter vi kan la en objektreferanse unnslippe, for eksempel å publisere objektet før det er fullstendig konstruert. Faktisk er dette en av de vanlige formene for flukt: når dette referanse rømmer under objektkonstruksjon.

Når dette referanse rømmer under konstruksjon, kan andre tråder se objektet i en upassende og ikke fullstendig konstruert tilstand. Dette kan igjen forårsake rare trådsikkerhetskomplikasjoner.

3. Rømmer med tråder

En av de vanligste måtene å la dette referanse flukt er å starte en tråd i en konstruktør. For å bedre forstå dette, la oss vurdere et eksempel:

public class LoggerRunnable implementerer Runnable {public LoggerRunnable () {Thread thread = new Thread (this); // dette slipper tråd.start (); } @ Override public void run () {System.out.println ("Started ..."); }}

Her passerer vi eksplisitt dette henvisning til Tråd konstruktør. Derfor, den nylig startede tråden kan kanskje se det vedlagte objektet før den er fullstendig konstruert. I samtidige sammenhenger kan dette føre til subtile feil.

Det er også mulig å passere dette referanse implisitt:

offentlig klasse ImplicitEscape {public ImplicitEscape () {Thread t = new Thread () {@Override public void run () {System.out.println ("Started ..."); }}; t.start (); }}

Som vist ovenfor, oppretter vi en anonym indre klasse avledet fra Tråd. Siden indre klasser opprettholder en referanse til sin omsluttende klasse, har dette referanse unnslipper igjen fra konstruktøren.

Det er ingenting galt med å lage en Tråd inne i en konstruktør. Det er imidlertid veldig motløs å starte det umiddelbart, som det meste av tiden, ender vi opp med en rømt dette referanse, enten eksplisitt eller implisitt.

3.1. Alternativer

I stedet for å starte en tråd inne i en konstruktør, kan vi erklære en dedikert metode for dette scenariet:

offentlig klasse SafePublication implementerer Runnable {private final Thread thread; offentlig SafePublication () {tråd = ny tråd (dette); } @ Override public void run () {System.out.println ("Started ..."); } offentlig ugyldig start () {thread.start (); }} ;:

Som vist ovenfor publiserer vi fortsatt dette henvisning til Tråd. Denne gangen starter vi imidlertid tråden etter at konstruktøren kommer tilbake:

SafePublication-publikasjon = ny SafePublication (); publikasjon.start ();

Derfor rømmer ikke objektreferansen til en annen tråd før den er fullstendig konstruert.

4. Konklusjon

I denne raske opplæringen, etter en kort introduksjon til den trygge publikasjonen, så vi hvorfor vi ikke skulle starte en tråd inne i en konstruktør.

Mer detaljert informasjon om publisering og flukt i Java finner du i Java Concurrency in Practice-boken.

Som vanlig er alle eksemplene tilgjengelige på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found