Klíčové slovo Volatile se používá k úpravě hodnoty proměnné různými vlákny. Používá se také k zajištění bezpečnosti pro vlákna tříd. To znamená, že metodu a instanci tříd může bez problému používat více vláken současně. Klíčové slovo nestálé lze použít s primitivním typem nebo objekty.
Klíčové slovo volatile neukládá hodnotu proměnné do mezipaměti a vždy čte proměnnou z hlavní paměti. Klíčové slovo nestálé nelze použít s třídami nebo metodami. Používá se však s proměnnými. Zaručuje také viditelnost a uspořádání. Zabraňuje kompilátoru v přeuspořádání kódu.
Obsah konkrétního registru zařízení se může kdykoli změnit, takže potřebujete klíčové slovo nestálé, abyste zajistili, že takové přístupy nebudou kompilátorem optimalizovány.
Příklad
class Test { static int var=5; }
Ve výše uvedeném příkladu předpokládejme, že dvě vlákna pracují na stejné třídě. Obě vlákna běží na různých procesorech, kde každé vlákno má svou lokální kopii var. Pokud nějaké vlákno změní svou hodnotu, změna se neprojeví v původním v hlavní paměti. To vede k nekonzistenci dat, protože druhé vlákno si není vědomo změněné hodnoty.
class Test { static volatile int var =5; }
Ve výše uvedeném příkladu jsou statické proměnné členy třídy, které jsou sdíleny mezi všemi objekty. V hlavní paměti je pouze jedna kopie. Hodnota nestálé proměnné nebude nikdy uložena do mezipaměti. Veškeré čtení a zápis se bude provádět z hlavní paměti a do hlavní paměti.
Kdy jej použít?
- Voltile proměnnou můžete použít, pokud chcete automaticky číst a zapisovat dlouhé a dvojité proměnné.
- Může být použit jako alternativní způsob dosažení synchronizace v Javě.
- Po dokončení operace zápisu uvidí všechna vlákna čtecího zařízení aktualizovanou hodnotu nestálé proměnné. Pokud nepoužíváte nestálé klíčové slovo, různé čtenářské vlákno může vidět různé hodnoty.
- Používá se k informování kompilátoru, že k určitému příkazu bude přistupovat více vláken. Zabraňuje kompilátoru provádět jakékoli změny pořadí nebo optimalizaci.
- Pokud nepoužíváte volatilní proměnnou kompilátor může změnit pořadí kódu, můžete zdarma zapsat do mezipaměti hodnotu volatilní proměnné místo čtení z hlavní paměti.
Důležité body
- Klíčové slovo těkavé můžete použít s proměnnými. Použití nestálého klíčového slova s třídami a metodami je nezákonné.
- Zaručuje, že hodnota volatilní proměnné bude vždy načtena z hlavní paměti, nikoli z místní mezipaměti vláken.
- Pokud jste proměnnou deklarovali jako nestálou, čtení a zápis jsou atomické
- Snižuje riziko chyby konzistence paměti.
- Jakýkoli zápis do těkavé proměnné v Javě se stane před vztahem s postupným čtením stejné proměnné.
- Nestálé proměnné jsou vždy viditelné pro ostatní vlákna.
- Nestálá proměnná, která je odkazem na objekt, může mít hodnotu null.
- Když proměnná není sdílena mezi více vlákny, nemusíte s touto proměnnou používat nestálé klíčové slovo.
Rozdíl mezi synchronizací a nestálým klíčovým slovem
Volatilní klíčové slovo nenahrazuje synchronizované klíčové slovo, ale může být v určitých případech použito jako alternativa. Existují následující rozdíly:
v pořádku
Nestálé klíčové slovo | Klíčové slovo synchronizace |
---|---|
Nestálé klíčové slovo je modifikátor pole. | Synchronizované klíčové slovo upravuje bloky kódu a metody. |
Vlákno nelze zablokovat pro čekání v případě nestálosti. | Vlákna lze v případě synchronizace zablokovat na čekání. |
Zlepšuje výkon vlákna. | Synchronizované metody snižují výkon vlákna. |
Synchronizuje hodnotu jedné proměnné najednou mezi pamětí vláken a hlavní pamětí. | Synchronizuje hodnotu všech proměnných mezi pamětí vláken a hlavní pamětí. |
Nestálá pole nepodléhají optimalizaci kompilátoru. | Synchronize podléhá optimalizaci kompilátoru. |
Příklad nestálého klíčového slova
V následujícím příkladu jsme definovali třídu, která zvyšuje hodnotu čítače. Metoda run () v VolatileThread.java získá aktualizovanou hodnotu a starou hodnotu, když vlákno začne vykonávat. V hlavní třídě definujeme pole vláken.
VolatileData.java
public class VolatileData { private volatile int counter = 0; public int getCounter() { return counter; } public void increaseCounter() { ++counter; //increases the value of counter by 1 } }
VolatileThread.java
VolatileThread.java public class VolatileThread extends Thread { private final VolatileData data; public VolatileThread(VolatileData data) { this.data = data; } @Override public void run() { int oldValue = data.getCounter(); System.out.println('[Thread ' + Thread.currentThread().getId() + ']: Old value = ' + oldValue); data.increaseCounter(); int newValue = data.getCounter(); System.out.println('[Thread ' + Thread.currentThread().getId() + ']: New value = ' + newValue); } }
VolatileMain.java
public class VolatileMain { private final static int noOfThreads = 2; public static void main(String[] args) throws InterruptedException { VolatileData volatileData = new VolatileData(); //object of VolatileData class Thread[] threads = new Thread[noOfThreads]; //creating Thread array for(int i = 0; i <noofthreads; ++i) threads[i]="new" volatilethread(volatiledata); for(int i="0;" < noofthreads; threads[i].start(); starts all reader threads threads[i].join(); wait for } pre> <p> <strong>Output:</strong> </p> <pre> [Thread 9]: Old value = 0 [Thread 9]: New value = 1 [Thread 10]: Old value = 1 [Thread 10]: New value = 2 </pre> <hr></noofthreads;>