Java:Gøre alle felter enten endelige eller flygtige?
Mens jeg føler mig private final
burde sandsynligvis have været standard for felter og variabler med et nøgleord som var
gør den foranderlig ved at bruge flygtig, når du ikke har brug for den
- meget langsommere, ofte omkring 10 gange langsommere.
- giver dig normalt ikke den trådsikkerhed, du har brug for, men kan gøre det sværere at finde sådanne fejl ved at gøre det mindre sandsynligt, at de dukker op.
- i modsætning til
final
hvilket forbedrer klarheden ved at sige, at dette ikke bør ændres ved hjælp afvolatile
når det ikke er nødvendigt, vil det sandsynligvis være forvirrende, da læseren forsøger at finde ud af, hvorfor det blev gjort flygtigt.
hvis feltet skal ændres (peg på et andet objekt, opdater den primitive værdi), så skal feltet være flygtigt, så alle andre tråde opererer på den nye værdi.
Selvom dette er fint at læse, så overvej denne trivielle sag.
volatile int x;
x++;
Dette er ikke trådsikkert. Da det er det samme som
int x2 = x;
x2 = x2 + 1; // multiple threads could be executing on the same value at this point.
x = x2;
Hvad værre er, er at bruge volatile
ville gøre denne slags fejl sværere at finde.
Som yshavit påpeger, er opdatering af flere felter sværere at omgås med volatile
for eksempel. HashMap.put(a, b)
opdaterer flere referencer.
Blot en synkronisering af metoderne, der får adgang til feltet, er utilstrækkelig, fordi de kan returnere en cachelagret værdi.
synchronized giver dig alle hukommelsesgarantierne på volatile
og mere, hvorfor det er væsentligt langsommere.
BEMÆRK:Bare synchronized
-at hver metode er heller ikke altid nok. StringBuffer
har alle metoder synkroniseret, men er værre end ubrugelige i en multi-threaded kontekst, da dens brug sandsynligvis er udsat for fejl.
Det er for nemt at antage, at opnåelse af trådsikkerhed er som at drysse eventyrstøv, tilføj noget magisk trådsikkerhed, og dine fejl forsvinder. Problemet er, at gevindsikkerheden er mere som en spand med mange huller. Tilslut de største huller, og fejlene kan se ud til at forsvinde, men medmindre du tilslutter dem alle, har du ikke trådsikkerhed, men det kan være sværere at finde.
Med hensyn til synkroniseret vs flygtig, siger dette
Andre mekanismer, såsom læsning og skrivning af flygtige variable og brugen af klasser i java.util.concurrent-pakken, giver alternative måder at synkronisere på.
https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html
Gør felter du ikke behøver at ændre final
er en god idé, uanset trådningsproblemer. Det gør forekomster af klassen lettere at ræsonnere om, fordi du nemmere kan vide, hvilken tilstand den er i.
Med hensyn til at gøre de andre felter til volatile
:
Blot en synkronisering af metoderne, der får adgang til feltet, er utilstrækkelig, fordi de kan returnere en cachelagret værdi.
Du vil kun se en cacheværdi, hvis du får adgang til værdien uden for en synkroniseret blok.
Alle adgange skal synkroniseres korrekt. Slutningen af en synkroniseret blok sker med garanti før starten af en anden synkroniseret blok (når der synkroniseres på den samme skærm).
Der er i det mindste et par tilfælde, hvor du stadig skal bruge synkronisering:
- Du vil gerne bruge synkronisering, hvis du skulle læse og derefter opdatere et eller flere felter atomært.
- Du kan muligvis undgå synkronisering for visse enkeltfeltopdateringer, f.eks. hvis du kan bruge en
Atomic*
klasse i stedet for en "almindelig gammel mark"; men selv for en enkelt feltopdatering kan du stadig kræve eksklusiv adgang (f.eks. tilføje et element til en liste, mens du fjerner et andet).
- Du kan muligvis undgå synkronisering for visse enkeltfeltopdateringer, f.eks. hvis du kan bruge en
- Den volatile/finale kan også være utilstrækkelig til ikke-trådsikre værdier, såsom en
ArrayList
eller en matrix.