Proč jsou synchronizované metody řešené zvláštním příznakem v bajtkódu? Je to jen syntaktický cukr pro synchronizovaný blok kolem celé metody nad this nebo this.getClass(), tak bych čekal, že se to tak také přeloží. Vypadá to, jakoby synchronizované bloky byly do bajtkódu přidány až později, přitom jsou snad v Javě od začátku…
Ano obaleni kodu cele metody do synchronized(this) je ekvivalentni z hlediska funkce - kdyz se na aplikaci divame z pohledu Javy, vnitrne je tam vsak rozdil.
Jsou tam minimalne dva duvody o kterych vim:
1) priznak nezabere zadne misto, je soucasti vetsiho bitoveho vektoru, kde jsou ulozena prava pristupu atd. Naproti tomu monitorenter a monitorexit + instrukce okolo jiz zaberou nekolik bajtu (rekneme 10) coz je napriklad pro synchronizovany setter overkill.
2) ted z hlavy nereknu jak to dela JIT, ale synchronizace metod se bere do uvahy pro inliningu.
Druhý problém související s volatilními atributy spočívá v tom, že změna hodnoty těchto atributů musí být viditelná i pro další vlákna, což znamená nutnost synchronizace vyrovnávacích pamětí (cache) jednotlivých mikroprocesorů či procesorových jader.
Ve skutečnosti všechny architektury (vyjma blackfin) mají cache automaticky synchronizovanou mezi procesory i jádry. Problém zde není synchronizace cache, problém je v tom, že procesor může přehazovat pořadí vykonávaných instrukcí. Proto se do kódu vkládají bariéry, které přehazování instrukcí zabraňují. Na cache však ty bariéry nemají vliv.
Prostý zápis do paměti moc nevadí, aplikuje se write-combine a několik zápisů do cache se do paměti provede najednou.
Zabijákem výkonnosti je
1) Přetahování více vláken o jednu cache-line (32 nebo 64 byte na adrese dělitelné 32 nebo 64) a to se děje už při prostém čtení a zápisu
2) Prefix LOCK
3) Nedejbože synchronizace na úrovni OS
Děkuji za pěkný článek.
Přemýšlím, zda tedy volatile vůbec k něčemu je.
Skutečně mám jako programátor někde použít volatile field? Co se místo toho radši synchronizovat na obalující instanci? Je to rychlejší (volatile místo synchronizace)?
K čemu dnes potřebuji volatile long, když mám AtomicLong (dtto pro int)?
A má nějaké užití volatile field neprimitivního typu (private volatile String)? V životě jsem to neviděl...
Volatile mělo vždycky tak nějak špatnou pověst. Přijde mi, že autoři JLS měli nějakou ideu, jak by to mělo fungovat, jenže se buď ukázalo, že to tak udělat úplně nejde, nebo vývoj procesorů nabral jiný směr... Pak trvalo několik verzí Javy (deset let?), než to alespoň začalo dělat co má, do toho vzniklo sun.misc.Unsafe, které umožnilo pár fíglů navíc (viz implementace AtomicLong, kde si jenom s volatile nevystačí) a z volatile je definitivně mrtvě narozené dítě...
Tak třeba kompilátor Scaly to používá pro lazy fields na double checked locking. Podívá se do volatile bitfieldu, jestli hodnota je již spočítaná. Pokud zjistí, že ano, nepotřebuje zamykat a může ji vrátit. Pokud ne, pokusí se zamknout a vyřešit to v rámci zámku. (Do té doby tam teoreticky může někdo zapsat.)
Vtip je v tom, že je to optimistický přístup - většinou to projde, takže to udělám tak, abych zamykat musel ideálně jen jednou. (Samozřejmě, když bude velký nával okolo prvního přístupu, tak to zpočátku nevyjde.)
A myslím, že se najde i mnoho dalších využití. Ale asi vždy spíše low-level.
Použití volatile atributů sice vede k určitým problémům - viz článek - ovšem pořád se pro ně najde použití. U volatile totiž požadujeme jen několik vlastností, především atomicitu čtení a zápisu (avšak ne dalších operací!) + "průpis" nové hodnoty do všech vláken + zákaz reorderingu.
AtomicLong a podobné třídy k tomu přidávají další podmínky navíc (atomicita inkrementace, ...), tudíž bude i implementace o něco složitější a JIT méně efektivní.
volatile String? no to je trošku podivné, protože se to vztahuje k referenčnímu typu, teď mě taky nenapadá nějaké použití.