Az atommƱveletek megjelenĂ©sĂ©nek elĆfeltĂ©telei
NĂ©zzĂŒk meg ezt a pĂ©ldĂĄt, hogy segĂtsen megĂ©rteni, hogyan mƱködnek az atomi mƱveletek:
public class Counter {
int count;
public void increment() {
count++;
}
}
Ha egy szĂĄlunk van, minden remekĂŒl mƱködik, de ha többszĂĄlat adunk hozzĂĄ, akkor rossz eredmĂ©nyt kapunk, Ă©s mindez azĂ©rt, mert a növelĂ©si mƱvelet nem egy mƱvelet, hanem hĂĄrom: egy kĂ©rĂ©s az aktuĂĄlis Ă©rtĂ©k lekĂ©rĂ©sĂ©re.szĂĄmol, majd növelje 1-gyel, Ă©s Ărjon Ășjra ideszĂĄmol.
Ăs ha kĂ©t szĂĄl növelni akar egy vĂĄltozĂłt, akkor nagy valĂłszĂnƱsĂ©ggel adatvesztĂ©sre kerĂŒl sor. Vagyis mindkĂ©t szĂĄl 100-at kap, ennek eredmĂ©nyekĂ©nt mindkettĆ 101-et Ăr a 102-es vĂĄrhatĂł Ă©rtĂ©k helyett.
Ăs hogyan lehet megoldani? ZĂĄrakat kell hasznĂĄlni. A szinkronizĂĄlt kulcsszĂł segĂt megoldani ezt a problĂ©mĂĄt, hasznĂĄlata garantĂĄlja, hogy egy szĂĄl egyszerre Ă©ri el a metĂłdust.
public class SynchronizedCounterWithLock {
private volatile int count;
public synchronized void increment() {
count++;
}
}
EzenkĂvĂŒl hozzĂĄ kell adnia a volatile kulcsszĂłt , amely biztosĂtja a hivatkozĂĄsok megfelelĆ lĂĄthatĂłsĂĄgĂĄt a szĂĄlak között. MunkĂĄssĂĄgĂĄt fentebb ĂĄttekintettĂŒk.
De mĂ©g mindig vannak ĂĄrnyoldalai. A legnagyobb a teljesĂtmĂ©ny, amikor sok szĂĄl prĂłbĂĄl zĂĄrolĂĄst szerezni, Ă©s az egyik ĂrĂĄsi lehetĆsĂ©get kap, a többi szĂĄl vagy blokkolva lesz, vagy felfĂŒggesztve lesz, amĂg a szĂĄl fel nem szabadul.
Mindezek a folyamatok, a blokkolĂĄsok, a mĂĄsik ĂĄllapotba vĂĄltĂĄs nagyon költsĂ©ges a rendszer teljesĂtmĂ©nye szempontjĂĄbĂłl.
AtommƱveletek
Az algoritmus alacsony szintƱ gĂ©pi utasĂtĂĄsokat hasznĂĄl, mint pĂ©ldĂĄul az összehasonlĂtĂĄs Ă©s csere (CAS, összehasonlĂtĂĄs Ă©s csere, amely biztosĂtja az adatok integritĂĄsĂĄt, Ă©s mĂĄr nagy mennyisĂ©gƱ kutatĂĄs folyik ezekrĆl).
Egy tipikus CAS-mƱvelet hårom operanduson mƱködik:
- MemĂłria a munkĂĄhoz (M)
- Egy vĂĄltozĂł meglĂ©vĆ vĂĄrhatĂł Ă©rtĂ©ke (A).
- Ăj Ă©rtĂ©k (B) beĂĄllĂtandĂł
A CAS atomikusan frissĂti M-et B-re, de csak akkor, ha M Ă©rtĂ©ke megegyezik A-val, kĂŒlönben nem törtĂ©nik semmi.
Az elsĆ Ă©s a mĂĄsodik esetben az M Ă©rtĂ©ke kerĂŒl visszaadĂĄsra, ami lehetĆvĂ© teszi hĂĄrom lĂ©pĂ©s kombinĂĄlĂĄsĂĄt, nevezetesen az Ă©rtĂ©k lekĂ©rĂ©sĂ©t, az Ă©rtĂ©k összehasonlĂtĂĄsĂĄt Ă©s frissĂtĂ©sĂ©t. Ăs mindez egyetlen mƱvelettĂ© vĂĄlik a gĂ©p szintjĂ©n.
Abban a pillanatban, amikor egy többszĂĄlĂș alkalmazĂĄs hozzĂĄfĂ©r egy vĂĄltozĂłhoz, Ă©s megprĂłbĂĄlja frissĂteni, Ă©s a CAS alkalmazĂĄsra kerĂŒl, akkor az egyik szĂĄl megkapja Ă©s frissĂteni tudja. A zĂĄrolĂĄsokkal ellentĂ©tben azonban mĂĄs szĂĄlak egyszerƱen hibaĂŒzenetet kapnak arrĂłl, hogy nem tudjĂĄk frissĂteni az Ă©rtĂ©ket. EzutĂĄn ĂĄttĂ©rnek a tovĂĄbbi munkĂĄra, Ă©s a vĂĄltĂĄs teljesen kizĂĄrt az ilyen tĂpusĂș munkĂĄknĂĄl.
Ebben az esetben a logika megnehezĂŒl amiatt, hogy kezelnĂŒnk kell azt a helyzetet, amikor a CAS mƱvelet nem mƱködött sikeresen. Csak Ășgy modellezzĂŒk a kĂłdot, hogy ne haladjon tovĂĄbb, amĂg a mƱvelet nem sikerĂŒl.
BevezetĂ©s az atomtĂpusokba
TalĂĄlkoztĂĄl mĂĄr olyan helyzettel, amikor be kell ĂĄllĂtani a szinkronizĂĄlĂĄst a legegyszerƱbb int tĂpusĂș vĂĄltozĂłhoz ?
Az elsĆ mĂłdszer, amelyet mĂĄr tĂĄrgyaltunk, a volatile + synchronized hasznĂĄlata . De vannak speciĂĄlis Atomic* ĂłrĂĄk is.

Ha CAS-t hasznĂĄlunk, akkor a mƱveletek gyorsabban mƱködnek az elsĆ mĂłdszerhez kĂ©pest. Ezen kĂvĂŒl speciĂĄlis Ă©s nagyon kĂ©nyelmes mĂłdszereink vannak az Ă©rtĂ©k hozzĂĄadĂĄsĂĄra, valamint a növelĂ©si Ă©s csökkentĂ©sĂ©re.
Az AtomicBoolean , AtomicInteger , AtomicLong , AtomicIntegerArray , AtomicLongArray olyan osztĂĄlyok, amelyekben a mƱveletek atomi jellegƱek. Az alĂĄbbiakban elemezzĂŒk a velĂŒk vĂ©gzett munkĂĄt.
AtomicInteger
Az AtomicInteger osztĂĄly mƱveleteket biztosĂt egy int Ă©rtĂ©kkel , amely atomian olvashatĂł Ă©s ĂrhatĂł, a kiterjesztett atomi mƱveletek mellett.
Olyan beszerzĂ©si Ă©s beĂĄllĂtĂĄsi mĂłdszereket tartalmaz , amelyek Ășgy mƱködnek, mint a vĂĄltozĂłk olvasĂĄsa Ă©s ĂrĂĄsa.
Vagyis âelĆtte törtĂ©nikâ ugyanazon vĂĄltozĂł bĂĄrmely kĂ©sĆbbi beĂ©rkezĂ©sĂ©vel, amelyrĆl korĂĄbban beszĂ©ltĂŒnk. Az atomiccompleteAndSet metĂłdus is rendelkezik ezekkel a memĂłriakonzisztencia jellemzĆkkel.
Minden olyan mƱvelet, amely Ășj Ă©rtĂ©ket ad vissza, atomosan törtĂ©nik:
int addAndGet (int delta) | Adott értéket ad az aktuålis értékhez. |
logikai összehasonlĂtĂĄsAndSet(elvĂĄrt int, frissĂtĂ©s int) | Az Ă©rtĂ©ket a megadott frissĂtett Ă©rtĂ©kre ĂĄllĂtja be, ha az aktuĂĄlis Ă©rtĂ©k megegyezik a vĂĄrt Ă©rtĂ©kkel. |
int decrementAndGet() | Eggyel csökkenti az aktuålis értéket. |
int getAndAdd(int delta) | A megadott értéket hozzåadja az aktuålis értékhez. |
int getAndDecrement() | Eggyel csökkenti az aktuålis értéket. |
int getAndIncrement() | Az aktuålis értéket eggyel növeli. |
int getAndSet(int newValue) | BeĂĄllĂtja a megadott Ă©rtĂ©ket, Ă©s visszaadja a rĂ©gi Ă©rtĂ©ket. |
int incrementAndGet() | Az aktuålis értéket eggyel növeli. |
lazySet(int newValue) | VĂ©gĂŒl ĂĄllĂtsa be a megadott Ă©rtĂ©ket. |
logikai gyengeCompareAndSet(vĂĄrhatĂł, frissĂtĂ©s int) | Az Ă©rtĂ©ket a megadott frissĂtett Ă©rtĂ©kre ĂĄllĂtja be, ha az aktuĂĄlis Ă©rtĂ©k megegyezik a vĂĄrt Ă©rtĂ©kkel. |
Példa:
ExecutorService executor = Executors.newFixedThreadPool(5);
IntStream.range(0, 50).forEach(i -> executor.submit(atomicInteger::incrementAndGet));
executor.shutdown();
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.HOURS);
System.out.println(atomicInteger.get()); // prints 50