De ahogy jobban megnézem, elvileg felemás értéket is visszaadhat, mert a második példából a h változó teljesen el is tűnhet az optimalizáció során. Vagy tévedek?
Beirom, mert tenyleg nem trivialis es nem is olyasmi amit az ember ki tudna bogaraszni anelkul hogy tovirol hegyire ismerne a JVM specifikaciot.
Szoval a kod a gyakorlatban szinte minden esetben a varhato visszateresi erteket adja, azonban olyan is elofordulhat hogy az elso ertek visszaadja a hash-t a masodik pedig 0-t!
A kovetkezo egy teljesen valid optimizacio egy JVM-tol:
Hát ezt most így fejből meg nem mondom (ha long lenne, akkor látom, hol a probléma), de leginkább arra céloztam, hogy ha egy objektumot több szálról is kell ráncigálni, akkor viszonylag egyszerű megoldani azt, hogy az történjen, amit akarsz.
Azt kibogarászni, hogy egy nem ilyesmire tervezett kód éppen miért hasal el, valóban nem triviális.
Ez csak annyiban befolyasol, mintha mondjuk egy intern Stringre sycnhronizalnal, ha van meg a kornyeken megegy hasonloan "okos", akkor egyszercsak nem erti az illeto mitol van a live/deaklock.
Ha szalkonkurenciaval foglalkozol akkor jeremy manson blogjat olvasd, o az egyik tarsszerzoje a java memory modelnek (Java5+), nala jobban kevesen ismerik.
A te konkrét esetedben az történt, hogy mindkét szál készített magának egy másolatot a static mezőről. Ezt megtehetik, mert nem tiltottad meg nekik a "volatile" kulcsszó használatával. Mindketten jól elvannak a maguk műsolataival, amivel hébe-hóba felülírják a változót.
Igen, errol kevesen tudnak: volatile nelkul nincs happens-before relacio biztositva thread interferencianal es nem garantalt az ertekek korrektsege.
Ez pl. egy olyan dolog, h az esetek 99,99%-ban nem fog hibat adni, csak majd vmikor amikor mar kinn van elesben a kod. Es emellett nehez elmagyarazni a legtobb fejlesztonek miert nem jo a kodja.
Crockl, nem akarok visszaelni a turelmeddel, de probalkoznal meg beleverni az en fejembe is a tudast? Sajnos nem vilagos, amit irtal. ("Nem ugyan azt az integer object-et kapod")
(Integer class-t megneztem)
(egyebirant megjegyzem, hogy a kod az utikalauzbol lett kimasolva. meglepo szamomra, hogy ekkora "elvi" hiba van a konyvben)
A te konkrét esetedben az történt, hogy mindkét szál készített magának egy másolatot a static mezőről. Ezt megtehetik, mert nem tiltottad meg nekik a "volatile" kulcsszó használatával. Mindketten jól elvannak a maguk műsolataival, amivel hébe-hóba felülírják a változót.
A synchronized elvileg jó lenne vele, de megint tökönszúrtad magad az autoboxing-unboxinggal. A k++-ból ugyanis valójában ez lesz: k = new Integer( k.intValue()+1).
Így aztán minden szál minden egyes iterációban egy másik objektumra szinkronizál.
A legnagyobb tanulság alighanem az, hogy sose használj Integer-t, ha műveletet végzel vele, és ha ide-vel dolgozol, tiltsd le az autoboxingot, mert azt a keveset, amit nyersz vele, bőven elveszíted az ilyen szívásokkal.
Egy valamivel kisebb tanulság, hogy szinkronizálást így szoktak csinálni a paranoiás fejlesztők:
class X { private final Object lock = new Object(); ... synchronized( lock ) { ... } ... }
Azért így, mert így semmilyen külső vagy belső tényező nem tudja megzavarni a szinkronizációt. Mivel private, külső osztály nem tud deadlockot okozni, és mivel final, nem esel bele abba a csapdába, amibe te most beleestél.
Ahogy a multkor emlitettem, az Integer egy immutable object. Amikor ++Integer-t hasznalsz akkor egy masik Integer objectet kapsz vissza. Innentol pedig mar nem el a monitorlock. Lock-olni final mezon erdemes(/kell). (en altalaban be is allitom a fordito erzekenyseget, hogy ha vki ilyet probalna le se forduljon).
lesd meg az Integer osztalyt. Nem ugyan azt az integer object-et kapod meg, igaz az ertek amit latsz az, egyforma, de megsem ugyan az az Integer objektum, ezert a lock az mukodik, csak epp nem ugy ahogy neked az kellene.
Lock-ra, ha valamiert ilyen kell, hasznalj sima Object-et private Object lock = new Object();
De ha már, akkor arra is érdemes emlékezni, hogy a lokális változók által tárolt referenciák maguktól megszűnnek akkor, amikor a változo scope-jából kikerül a végrehajtás.
Ez annak a termeszetes kovetkezmenye h out of scope nem leteznek, tehat null ertekuek. Ez alapbol nem tul bonyolult de tobbszalu programok eseten mar konnyen adodhatnak felreertesek. A most hozzaadott closure-k eseten szinten adodhatnak bonyolultabb szituaciok.
Abból indultam ki, hogy akinek új ez a referenciásdi, az nem indít helyből egy WeakHashMap-pel. :)
De ha már, akkor arra is érdemes emlékezni, hogy a lokális változók által tárolt referenciák maguktól megszűnnek akkor, amikor a változo scope-jából kikerül a végrehajtás.
A *-os reszhez annyi kiegeszito, hogy ez a graf a strongreference eleire ervenyes. A soft, weak es phantom reference elek eseten mar nem vagy nem feltetlenul.
A fontos tudnivaló, hogy itt minden más, mint a C++-ban.
Referencia nem törlődik magától. Vagy kézzel állítod null-ra, vagy úgy marad örökre.* Ha egy objektumra nincs több élő referencia, előbb-utóbb a gc magától összeszedi, tehát nem kell kézzel hívogatni. (Jobb helyeken le is van tiltva a kézi aktiválása, mert csak rontasz a helyzeten.)
A dispose() elvileg az ablakhoz kötődő natív erőforrásokat (buffereket, window handle-öket meg a franc tudja még mit) szabadítja fel ettől teljesen függetlenül. Feltételezem, hogy ezek az izék lazy inittel vannak megoldva, tehát dispose() után újrainicializálódnak, ha meghívod a megfelelő metódusokat.
A megoldás, hogy a dispose() meghívása után eldobsz minden, a frame-re mutató referenciát.
*Fontos tudni, hogy mi számít élő referenciának Javaban. A definíció szerint élő az a referencia, ami egy futó, vagy még el nem indult szálból kiindulva elérhető a referenciagráf irányított élein lépkedve. Ez azért jó, mert egyrészt nem kell aggódni a körkörös referencia miatt, másrészt meg nem kell egy objektum minden mezejét kinullázni, ha tudod, hogy magára az objektumra mutató referencia meg fog szűnni egyébként is.
En nem vagyok egy nagy GUI szakerto, de NevemTevenek igaza van. Amig van ervenyes (Strong)Reference egy object-re addig az per definicio nem kerulhet gc-re. Nem vagyok tisztaban a JDesktop mukodesevel meg most nincs is idom hogy utana nezzek, de jo esellyel ha volt egy .add() hivas akkor nala is lesz egy ervenyes referencia az objektumhoz. Azonkivul en a JInternalFrame javadocjaban nem latok semmi olyat ami miatt a dispose() hivasa utan neki null-nak kene lennie.