Java >> Java tutorial >  >> Tag >> volatile

Doven initialisering uden synkronisering eller flygtigt nøgleord

Denne udtalelse lyder lidt kryptisk. Jeg gætter dog på, at HLS refererer til tilfældet, når du dovent initialiserer et instansfelt og er ligeglad med, om flere tråde udfører denne initialisering mere end én gang.
Som et eksempel kan jeg pege på hashCode() metode til String klasse:

private int hashCode;

public int hashCode() {
    int hash = hashCode;
    if (hash == 0) {
        if (count == 0) {
            return 0;
        }
        final int end = count + offset;
        final char[] chars = value;
        for (int i = offset; i < end; ++i) {
            hash = 31*hash + chars[i];
        }
        hashCode = hash;
    }
    return hash;
}

Som du kan se adgang til hashCode feltet (som indeholder cacheværdien af ​​den beregnede String-hash) er ikke synkroniseret, og feltet er ikke erklæret som volatile . Enhver tråd, der kalder hashCode() metoden vil stadig modtage den samme værdi, selvom hashCode felt kan skrives mere end én gang af forskellige tråde.

Denne teknik har begrænset anvendelighed. IMHO det er mest brugbart til tilfælde som i eksemplet:et cachelagret primitivt/uforanderligt objekt, som er beregnet fra de andre endelige/uforanderlige felter, men dets beregning i konstruktøren er en overkill.


Hrm. Som jeg læser dette er det teknisk forkert, men okay i praksis med nogle forbehold. Kun endelige felter kan sikkert initialiseres én gang og tilgås i flere tråde uden synkronisering.

Dovne initialiserede tråde kan lide af synkroniseringsproblemer på en række måder. For eksempel kan du have konstruktør-race-betingelser, hvor referencen til klassen er blevet eksporteret uden selve klassen initialiseres fuldt ud.

Jeg tror, ​​det afhænger meget af, om du har et primitivt felt eller et objekt. Primitive felter, der kan initialiseres flere gange, hvor du ikke har noget imod, at flere tråde udfører initialiseringen, ville fungere fint. Dog HashMap stilinitialisering på denne måde kan være problematisk. Selv long værdier på nogle arkitekturer kan gemme de forskellige ord i flere operationer, så kan eksportere halvdelen af ​​værdien, selvom jeg har mistanke om, at en long ville aldrig krydse en hukommelsesside, så derfor ville det aldrig ske.

Jeg tror, ​​det afhænger meget af, om et program har noget eller ej hukommelsesbarrierer -- enhver synchronized blokeringer eller adgang til volatile felter. Djævelen er bestemt i detaljerne her, og koden, der udfører den dovne initialisering, fungerer muligvis fint på én arkitektur med et sæt kode og ikke i en anden trådmodel eller med en applikation, der sjældent synkroniserer.

Her er et godt stykke om endelige felter til sammenligning:

http://www.javamex.com/tutorials/synchronization_final.shtml

Fra og med Java 5 er en bestemt brug af det endelige nøgleord et meget vigtigt og ofte overset våben i dit samtidighedsvåben. I det væsentlige kan final bruges til at sikre, at når du konstruerer et objekt, vil en anden tråd, der får adgang til det objekt, ikke se objektet i en delvist konstrueret tilstand, som det ellers kunne ske. Dette skyldes, at når den bruges som en attribut på variablerne af et objekt, har final følgende vigtige egenskaber som en del af sin definition:

Nu, selvom feltet er markeret som endeligt, hvis det er en klasse, kan du ændre felterne indenfor klassen. Dette er et andet problem, og du skal stadig have synkronisering til dette.


Dette fungerer fint under nogle forhold.

  • det er okay at prøve at indstille feltet mere end én gang.
  • det er okay, hvis individuelle tråde ser forskellige værdier.

Ofte når du opretter et objekt som ikke er ændret f.eks. indlæsning af en egenskaber fra disk, at have mere end én kopi i en kort periode er ikke et problem.

private static Properties prop = null;

public static Properties getProperties() {
    if (prop == null) {
        prop = new Properties();
        try {
            prop.load(new FileReader("my.properties"));
        } catch (IOException e) {
            throw new AssertionError(e);
        }
    }
    return prop;
}

På kort sigt er dette mindre effektivt end at bruge låsning, men på lang sigt kan det være mere effektivt. (Selvom Properties har sin egen lås, men du forstår godt;)

IMHO, det er ikke en løsning, der virker i alle tilfælde.

Måske er pointen, at du i nogle tilfælde kan bruge mere afslappede hukommelseskonsistensteknikker.


Java tag