Javában hamar hozzá fogsz szokni ahhoz, hogy mindenhez wrappert csinálj. Sokakat bosszant, de szerintem ha nem ész nélkül csinálja az ember, a végeredmény sokkal olvashatóbb.
igen ez volt a cel. A benazasom onnan indult, hogy nem akartam sajat osztalyt kesziteni egy nyomoronc counter kedveert, es olyan siman elsiklottam az Integer immutable volta felett, hogy meg magam is kinevettem magam. Nem megy sajat "Integer" class nelkul a dolog.
(Koszonet el Papinak, hogy ismetelten folhivta erre a figyelmemet. )
Aztan mostmar megemesztettem azt is, hogy ezen a modon ahogy en csinatam nem eleg, ha az egyik szal hasznal egy lockot. Itt talan megis objektum orientalt vagyok :D , ha nagyon szeretnem egy objektumhoz kotni a lockolas problematikajat, es mas objektumnal ami hozzaferne a kritikus szalbiztos valtozohoz, mar nem szeretnek mindenfele lockokkal bajlodni, a tobbi szal ne is tudjon a dologrol, legyen az a szalbiztos valtozot tartalmazo obj dolga.
Ez viszont valoban csak szinkronizalt setter/getter metodusokkal megy, nem szinkronizalhatok a valtozora.
Tobb problema van: statikus hivatkozas instance-on keresztul. az objektum orientalt nyelvbol eredoen nem modositunk/olvasunk direkt valtozokat, plane nem multthreading kornyezetben. Egy mutexet nagyon nem szerencses kulonbozo classokban hasznalni. En csinaltam anno ilyet elsosorban performance okok miatt, de ezzel gyakorlatilag oda is lett a kodom karbantarthatosaga. Nagyon kenyes dolog az ilyesmit debugolni, es akkor gondolj bele abba, ha nem is a sajat kododrol van szo. Szoval mindig igyekezz hazon (classon) belul tartani a thread safety-t biztosito kodot.
Nincs problema. Egy szerencsetlen szamlalot probalok ket szallal toszogatni ugy, hogy bizonyos helyeken a kodban biztonsagban erezzem magam, ezeken a helyeken ne kaphasson lehetoseget mas szal.
Amennyiben az a celod, hogy a SimpleCount1 peldanyod ciklusmagjanak mukodese olyan legyen hogy minden iteracional kiirt szampar kozti kulonbseg 1 legyen, akkor igen.
Ehhez az kene, hogy a SimpleCount2-ben szereplo MyInteger modosito muvelet is ugyanazon lock-ot szerezze meg. azaz synchronized(SimpleCount1.myInteger) {...}
Ez kivitelezheto, de nagyon nem ajanlott ilyet csinalni.
Ez egyaltalan nem thread safe. A SimpleCount2 peldanyod minden akadaly nelkul fogja a MyInteger-edet modositani, es SimpleCount1 peldanya is, mivel csak o hasznalja a lockot.
Ha multithread kornyezetben egy valtozot tobb thread hasznal egyszerre, akkor biztositani kell explicit vagy implicit a szalbiztonsagot. "Magatol" nem lesz szalbiztos. A te esetedre ott van az AtomicInteger amivel implicit biztositod a szalbiztonsagot. Ha olyan adattipust hasznalsz ahol ez nincs biztositva, ott ez neked kell explicit elvegezni.
public void setK(int k){ synchronized(mutex){ this.k=k; } }
ugy kerulod meg a problemat, hogy nem engeded, hogy egy masik szal kozvetlenul hozzaferjen a k valtozohoz, csak a publikalt es szinkronizalt setK() metodussal.
tehat nem k-ra szinronizalsz (primitivre ugyebar nem is lehet), hanem eldugod k-t, hogy hozza se ferjen semmifele szal, es a beallito metodust szinkronizalod, egy csak erre a szinkronizalcio celjara gyartott mutex-szel.
Tehat megkerulod a problemat. :)
Akkor az nem muxik, hogy egy pl osztalyszintu publikus, statikus countert szalbiztosan kezeljunk kozvetlenut setter()/getter() metodusok kihagyasaval?
el Papi! Megprobalom osszfoglalni a korabbiakat: Idaig azon gorcsoltunk, hogy Integer nem, Object mutex igen, meg hogy ertheto okbol ez azert legyen static...
Object mutex nem ok, ez kiderul, ha jol latom. Igy van?
Javitsatok ki, ha nem jol gondolom, most tehat ott tartunk, hogy a mutexes megoldas zsakutca, vegyuk elo a synchronizalt getter-setter megoldast, es csak ne nyulkajak k-hoz mas szalakbol publikusan.
Class MyClass{ private int k;
public synchronized void setK(int k){ this.k=k; }
public synchronized int getK(){ return this.k; } }
es ugyanez kulso monitorlockal:
Class MyClassWithMutex{ private int k;
private final Object mutex = new Object();
public void setK(int k){ synchronized(mutex){ this.k=k; } }
public synchronized int getK(){ synchronized(mutex){ return this.k; } } }
A kulonbseg az hogy az elso verzioban synchronized(this) a masodikban pedig synchronized(mutex) van.
angyalhentes kodja es magyarazta nem eleg vilagos nekem (talan nemi eliras is lehet benne). Picit lehetne reszletesebben, kezdok szamara is erthetobben?
class SyncInt { private final Object lock = new Object(); private int k;
public final void execute( SyncBlock block) { synchronized( block ) { k = block.syncExecute( k ); } }
}
interface SyncBlock { public int syncExecute( int currentValue ); }
Mas:
"Ugy hogy a counter-t private mezove teszed es enkapszulalod a modosito muveleteit. Ezt akar egy synchronizalt getter-setter komboval is el lehet erni."
el Papi! Megprobalom osszfoglalni a korabbiakat: Idaig azon gorcsoltunk, hogy Integer nem, Object mutex igen, meg hogy ertheto okbol ez azert legyen static...
Object mutex nem ok, ez kiderul, ha jol latom. Igy van?
Javitsatok ki, ha nem jol gondolom, most tehat ott tartunk, hogy a mutexes megoldas zsakutca, vegyuk elo a synchronizalt getter-setter megoldast, es csak ne nyulkajak k-hoz mas szalakbol publikusan.
Igen, erre probaltam felhivni a figyelmet :-) , de eppen ezert mondtam, hogy egy olyan kodnal amit teljes mertekben az fejleszto kontrollja alatt van, ott nem erdemes pl. extra interface-kkel bonyolitani, egyszeruen ossze kell hangolni a mutex-et.
Ha viszont van egy public szolgaltatasod amihez van egy API aminek command pattern jelleggel lehet utasitasokat kuldeni neki, akkor ott viszont nagyon kell figyelni, ne adjon az ember teret semelyik "kreativ" fejlesztotarsanak sem.
Ez garantálja azt, hogy amíg a syncExecute() fut, addig senki nem piszkál hozzá k-hoz. A különböző műveleteid mind a SyncBlock implementációi lesznek, de ez nem okoz gondot, mert másképp nem lehet hozzáférni k értékéhez.
Ha nagyon szorszalhasogatoak vagyunk akkor ez sem teljesen igaz. :-) Reflection illetve bytecode injection nevu modszerekkel ez is megkerulheto. Erre akkor kell figyelni ha vmilyen public API-t adsz ki.
bocs az eleje visszavonva, baromsag. a ++, -- masolat miatt persze hogy elcsuszik, csak maga az 1-1 muvelet atomic, de itt ugye ertek adasnal nem csak 1 van beloluk.
ebben az esetben nem igaz amit mondasz, marmint a volatile itt garantalja, hogy egyszerre csak 1 irhat-ja. A peldadban is, a vegeredmeny-ben 0 - 0-at fogsz kapni.
Ami nem atomic, hogy ebben az esetben a kiirataskor, mit latsz, mert az nyilvan el fog csuszni, hiszen az azon mulik, hogy epp mit olvas ki, viszont a valtozo mindenkori erteke az ebben az esetben atomic.
futtasd le: public void run() { for (int i = 1; i <= 30; ++i) { if (getName().equals("t1")) { k++; } else { k--; } System.out.println(getName() + ": " + k); } }
public int getK() { return k; }
main-ben sleep-ej picit run utan, amig veget nem ertnek a thread-ek, es irast ki a getK()-t es meglatod, mindket thread 0 -at mutat ra.
De lasd: http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html 8.3.1.4 volatile Fields Es itt a Te peldad is: http://jeremymanson.blogspot.com/2007/08/volatile-does-not-mean-atomic.html
Szoval abban igazad, van, hogy a kiolvasas nem atomic, azaz nem feltetlen azt az erteket latod, amit elotte noveltel, viszont maga a k az atomic modosul, tehat nem hibazik.
Ergo ha szuksegunk van kozvetlen utana kiolvasni is ugyan azt az erteket, amire modositottuk, akkor kell syncronized , vagy ami meg jobb ebben az esetben az Atomic class-ok.
De a volatile-t legintkabb nem erre hasznalhatjuk, hanem thread safe flag-ekre. Egy thread beallitja, hogy true, akkor az ez utan lefuto masik thread mely ezt a valtozot akarja kiolvasni azt fogja latni, hogy true, es nem kell a draga koltsegu syncronized-ot hasznalni.
class SyncInt { private final Object lock = new Object(); private int k;
public final void execute( SyncBlock block) { synchronized( block ) { k = block.syncExecute( k ); } }
}
interface SyncBlock { public int syncExecute( int currentValue ); }
Ez garantálja azt, hogy amíg a syncExecute() fut, addig senki nem piszkál hozzá k-hoz. A különböző műveleteid mind a SyncBlock implementációi lesznek, de ez nem okoz gondot, mert másképp nem lehet hozzáférni k értékéhez.
Erre persze csak akkor van szükség, ha előre nem látható számú megbízhatatlan műveleted lesz, egyébként a műveleteidet egyszerűen csak a SyncInt metódusaiként definiálod, és te magad garantálod azt, hogy a synchronized() blokk mindig jó helyen legyen.