Geçici anahtar kelime ne işe yarar?


672

Bugün iş yerinde, volatileJava'daki anahtar kelimeyle karşılaştım. Çok aşina olmadığım için şu açıklamayı buldum:

Java teorisi ve uygulaması: Oynaklığı yönetme

Bu makalenin söz konusu anahtar kelimeyi açıkladığı ayrıntı göz önüne alındığında, bunu hiç kullandınız mı veya bu anahtar kelimeyi doğru bir şekilde kullanabileceğiniz bir durum gördünüz mü?

Yanıtlar:


742

volatilebellek görünürlüğü için semantiğe sahiptir. Temel olarak, bir volatilealanın değeri, üzerine yazma işlemi tamamlandıktan sonra tüm okuyucular tarafından (özellikle diğer iş parçacıkları) görünür hale gelir. Olmadan volatile, okuyucular güncellenmemiş bir değer görebiliyordu.

Sorunuzu cevaplamak için: Evet, volatilebazı kodların döngü devam edip etmediğini kontrol etmek için bir değişken kullanıyorum . Döngü volatiledeğeri test eder ve varsa devam eder true. Koşul, falsebir "durdurma" yöntemi çağrılarak ayarlanabilir . Döngü false, stop yöntemi yürütmeyi tamamladıktan sonra değeri test ettiğinde görür ve sona erer.

Tavsiye ettiğim " Uygulamada Java Eşzamanlılığı " kitabı iyi bir açıklama veriyor volatile. Bu kitap, soruda atıfta bulunulan IBM makalesini yazan aynı kişi tarafından yazılmıştır (aslında kitabını o makalenin alt kısmında belirtmektedir). Benim kullanımımvolatile makalesinde "kalıp 1 durum bayrağı" dediğidir.

volatileKaputun altında nasıl çalıştığı hakkında daha fazla bilgi edinmek istiyorsanız , Java bellek modelini okuyun . Bu seviyenin ötesine geçmek istiyorsanız, Hennessy & Patterson gibi iyi bir bilgisayar mimarisi kitabına bakın ve önbellek tutarlılığı ve önbellek tutarlılığı hakkında bilgi edinin.


118
Bu cevap doğru, ancak eksik. volatileJSR 133'te tanımlanan yeni Java Bellek Modeli ile birlikte gelen önemli bir özelliği atlar : bir iş parçacığı bir volatiledeğişkeni okuduğunda, yalnızca başka bir iş parçacığının kendisine yazdığı son değeri değil, diğer tüm değişkenlerin de volatileyazma sırasında o diğer evrede görülebiliyordu . Bkz bu cevabı ve bu referansı .
Adam Zalcman

46
Yeni başlayanlar için, bazı kodlarla göstermenizi rica ediyorum (lütfen?)
Hungry Blue Dev

6
Soruda bağlantılı makalede kod örnekleri bulunmaktadır.
Greg Mattes

Bence 'Hennessy & Patterson' bağlantısı koptu. Ve 'Java bellek modeli' bağlantısı aslında Oracle'ın Java Dil Spesifikasyonu 'Bölüm 17. Konular ve Kilitler'e yol açar.
Kris

2
@fefrei: “hemen” konuşma diline özgü bir terimdir. Tabii ki, ne yürütme zamanlaması ne de iş parçacığı zamanlama algoritmaları gerçekten belirtilmediğinde bu garanti edilemez. Bir programın uçucu bir okumanın belirli bir uçucu yazma işleminden sonra olup olmadığını öğrenmesinin tek yolu, görülen değerin beklenen yazılı olan olup olmadığını kontrol etmektir.
Holger

177

“… Uçucu değiştirici, bir alanı okuyan herhangi bir iş parçacığının en son yazılan değeri görmesini garanti eder.” - Josh Bloch

Kullanmayı düşünüyorsanız volatile, java.util.concurrentatom davranışı ile ilgili paketi okuyun . Singleton Paternindeki

Wikipedia yayını kullanımda uçucu olduğunu gösterir.


18
Neden hem var volatileve synchronizedanahtar?
ptkato

5
Bir Singleton Paternindeki Wikipedia makalesi o zamandan beri çok değişti ve volatileartık söz konusu örneği artık içermiyor . Bu bulunabilir arşivlenmiş sürümde .
bskp

1
@ ptkato Bu iki anahtar kelime tamamen farklı amaçlara hizmet eder, bu nedenle her ikisi de eşzamanlılık ile ilgili olsa da, soru karşılaştırma olarak çok mantıklı değildir. Bu "Neden orada hem demek gibi voidve publicanahtar kelimeler".
DavidS

134

Hakkında önemli nokta volatile:

  1. Java Senkronizasyon Java anahtar kelimeler kullanarak mümkündür synchronizedve volatileve kilitler.
  2. Java'da synchronizeddeğişkenimiz olamaz . Kullanılması synchronizedbir değişkene sahip anahtar kelimeyi yasa dışıdır ve derleme hatasına neden olur. synchronizedJava'da değişkeni kullanmak yerine, volatileJVM iş parçacıklarının değerini okumasını söyleyen java değişkenini kullanabilirsiniz .volatile ana bellekten değişkenin yerel olarak önbelleğe almasını istemeyen .
  3. Bir değişken birden fazla evre arasında paylaşılmazsa, volatileanahtar kelimeyi kullanmaya gerek yoktur .

kaynak

Örnek kullanım volatile:

public class Singleton {
    private static volatile Singleton _instance; // volatile variable
    public static Singleton getInstance() {
        if (_instance == null) {
            synchronized (Singleton.class) {
                if (_instance == null)
                    _instance = new Singleton();
            }
        }
        return _instance;
    }
}

İlk istek geldiğinde tembel bir şekilde örnek yaratıyoruz.

_instanceDeğişkeni yapmazsak volatile, örneğini oluşturan Evre Singletondiğer evre ile iletişim kuramaz. Dolayısıyla A İş parçacığı Singleton örneği oluşturuyorsa ve oluşturma işleminden hemen sonra CPU bozulursa, diğer tüm iş parçacıkları_instance null değil olarak yine de null atandığına inanırlar.

Bu neden oluyor? Okuyucu iş parçacıkları kilitleme yapmadığından ve yazar iş parçacığı senkronize edilmiş bir bloktan çıkıncaya kadar, bellek senkronize edilmeyecek ve değeri _instanceana bellekte güncellenmeyecektir. Java'daki Volatile anahtar sözcüğüyle bu, Java'nın kendisi tarafından işlenir ve bu güncellemeler tüm okuyucu dizileri tarafından görülebilir.

Sonuç : volatileanahtar kelime, evreler arasındaki bellek içeriğini iletmek için de kullanılır.

Uçucu olmadan kullanım örneği:

public class Singleton{    
    private static Singleton _instance;   //without volatile variable
    public static Singleton getInstance(){   
          if(_instance == null){  
              synchronized(Singleton.class){  
               if(_instance == null) _instance = new Singleton(); 
      } 
     }   
    return _instance;  
    }

Yukarıdaki kod iş parçacığı için güvenli değildir. Eşitlenmiş blok içinde örneğin değerini bir kez daha kontrol etmesine rağmen (performans nedenleriyle), JIT derleyicisi, bayt kodunu, kurucu yürütmeyi tamamlamadan önce örneğe yapılan başvurunun ayarlandığı şekilde yeniden düzenleyebilir. Bu, getInstance () yönteminin tamamen başlatılmamış olabilecek bir nesneyi döndürdüğü anlamına gelir. Kodu iş parçacığı açısından güvenli hale getirmek için, değişken değişkeni için Java 5'ten beri geçici anahtar kelime kullanılabilir. Uçucu olarak işaretlenen değişkenler, yalnızca nesnenin kurucusu yürütülmesini tamamladıktan sonra diğer evreler tarafından görülebilir.
Kaynak

resim açıklamasını buraya girin

volatileJava'da kullanım :

Hata hızlı yineleyiciler genelliklevolatile liste nesnesindeki bir sayaç kullanılarak uygulanır .

  • Liste güncellendiğinde sayaç artırılır.
  • Bir Iteratoroluşturulduğunda, sayacın geçerli değeri Iteratornesneye gömülür .
  • Bir Iteratorişlem gerçekleştirildiğinde, yöntem iki sayaç değerini karşılaştırır ve ConcurrentModificationExceptionfarklıysa a atar .

Arıza güvenlikli yineleyicilerin uygulanması tipik olarak hafiftir. Genellikle belirli liste uygulamasının veri yapılarının özelliklerine güvenirler. Genel bir örüntü yoktur.


2
"Hata-hızlı yineleyiciler genellikle uçucu bir sayaç kullanılarak uygulanır" - artık durum artık çok pahalı değil: bugs.java.com/bugdatabase/view_bug.do?bug_id=6625725
Vsevolod Golovanov

_instance için çift kontrol güvenli midir? ben bile uçucu ile güvenli olmadığını düşündüm
Dexters

"JVM iş parçacıklarına, ana bellekten değişken değişken değerini okuma talimatı verir ve bunu yerel olarak önbelleğe almaz." iyi bir nokta
Humoyun Ahmad

İplik güvenliği için private static final Singleton _instance;de kullanılabilir.
Chris311

53

volatile konuları durdurmak için çok kullanışlıdır.

Kendi iş parçacıklarınızı yazmanız gerekmiyor, Java 1.6'nın çok güzel iş parçacığı havuzları var. Ancak bir konuya ihtiyacınız olduğundan eminseniz, nasıl durduracağınızı bilmeniz gerekir.

İplikler için kullandığım desen:

public class Foo extends Thread {

  private volatile boolean close = false;

  public void run() {
    while(!close) {
      // do work
    }
  }
  public void close() {
    close = true;
    // interrupt here if needed
  }
}

Yukarıdaki kod segmentinde closewhile döngüsünde okunan evre , çağırandan farklıdır close(). Uçucu olmadan, döngüyü çalıştıran iş parçacığı hiçbir zaman kapanacak değişikliği görmeyebilir.

Senkronizasyona nasıl gerek olmadığına dikkat edin


2
Bunun neden gerekli olduğunu merak ediyorum. Sadece diğer iş parçacıklarının bu iş parçacığının durum değişikliğine iş parçacığı senkronizasyonu tehlikeye girecek şekilde tepki vermesi gerekmiyor mu?
Jori

27
@Jori, while döngüsünde yakın okuma iş parçacığı close () çağıran iş parçacığından farklı olduğu için değişken olmalıdır. Uçucu olmadan, döngüyü çalıştıran iş parçacığı hiçbir zaman kapanacak değişikliği görmeyebilir.
13'te Pirolistik

böyle bir iş parçacığını durdurma veya İş parçacığı # interrupt () ve İş parçacığı # isInterrupted () yöntemlerini kullanma arasında bir avantaj olduğunu söyleyebilir misiniz?
Ricardo Belchior

2
@Pyrolistical - Eğer iplik gözlenen mı asla pratikte değişimleri gördükten? Veya bu sorunu güvenilir bir şekilde tetiklemek için örneği genişletebilir misiniz? Merak ediyorum çünkü temelde örneğe özdeş ancak volatileanahtar kelime olmadan kod kullandığımı (ve başkalarını kullanarak gördüm) biliyorum ve her zaman iyi çalışıyor gibi görünüyor.
aroth

2
@aroth: günümüzün JVM'leri ile, uygulamada, en basit örneklerle bile, ancak bu davranışı güvenilir bir şekilde üretemeyeceğinizi gözlemleyebilirsiniz . Daha karmaşık uygulamalarda, bazen kodun içinde çalışmasını sağlayan bellek görünürlüğü garantileri olan başka eylemleriniz de vardır, bu da neden çalıştığını bilmediğiniz için özellikle tehlikelidir ve kodunuzdaki basit, görünüşte ilgisiz bir değişiklik, uygulama…
Holger

31

Kullanmanın yaygın bir örneği, volatilebir volatile booleandeğişkeni bir iş parçacığını sonlandırmak için bayrak olarak kullanmaktır . Bir iş parçacığını başlattıysanız ve farklı bir iş parçacığından güvenli bir şekilde kesebilmek istiyorsanız, iş parçacığının periyodik olarak bir bayrağı kontrol etmesini sağlayabilirsiniz. Durdurmak için bayrağı true olarak ayarlayın. Bayrağı yaparak volatile, denetleyen iş parçacığının, bir sonraki kontrolünde synchronizedblok kullanmadan ayarlandığını görmesini sağlayabilirsiniz .


27

volatileAnahtar kelime ile bildirilen bir değişken , onu özel yapan iki ana niteliğe sahiptir.

  1. Uçucu bir değişkenimiz varsa, bilgisayarın (mikroişlemci) önbellekine herhangi bir iş parçacığı tarafından önbelleğe alınamaz. Erişim her zaman ana bellekten gerçekleşti.

  2. Uçucu bir değişken üzerinde bir yazma işlemi varsa ve aniden bir okuma işlemi istenirse, yazma işleminin okuma işleminden önce tamamlanması garanti edilir .

Yukarıdaki iki nitelik bunu

  • Bir değişken değişken okuyan tüm evreler kesinlikle en son değeri okuyacaktır. Çünkü hiçbir önbellek değeri onu kirletemez. Ayrıca okuma talebi sadece mevcut yazma işleminin tamamlanmasından sonra verilecektir.

Öte yandan,

  • Daha önce bahsettiğim # 2'yi araştırırsak, volatileanahtar kelimenin 'n' sayıda okuyucu iş parçacığı ve ona erişmek için yalnızca bir yazar iş parçacığı olan paylaşılan bir değişkeni korumanın ideal bir yolu olduğunu görebiliriz. volatileAnahtar kelimeyi ekledikten sonra yapılır. İplik güvenliği ile ilgili başka bir yük yok.

Conversly,

Biz olamaz faydalanmak volatilesahip paylaşılan değişkeni karşılamak için, sadece anahtar kelime erişen birden fazla yazar parçacığı .


3
Bu, uçucu ve senkronize arasındaki farkı açıklar.
ajay

13

Hiç kimse uzun ve çift değişkenli tip için okuma ve yazma işleminden bahsetmemiştir. Okuma ve yazma işlemleri, atomik işlemler için uçucu anahtar sözcüğü kullanması gereken uzun ve çift değişkenli türler dışında, referans değişkenler ve çoğu ilkel değişken için atomik işlemlerdir. @link


Daha da netleştirmek için, bir boole uçucusunun ayarlanmasına gerek yoktur, çünkü bir booleanın okuma ve yazma ZATEN atomiktir.
Kai Wang

2
@KaiWang booleanlarda atomisite amacıyla uçucu madde kullanmanıza gerek yoktur. Ancak kesinlikle görünürlük nedenleriyle olabilirsiniz. Söylemek istediğin bu mu?
SusanW

12

Evet, değişken bir değişkene birden çok iş parçacığı tarafından erişilmesini istediğinizde uçucu kullanılmalıdır. Çok yaygın bir usecase değildir, çünkü tipik olarak tek bir atomik işlemden daha fazlasını yapmanız gerekir (örneğin değiştirmeden önce değişken durumunu kontrol edin), bu durumda bunun yerine senkronize bir blok kullanırsınız.


10

Kanımca, uçucu anahtar kelimenin kullanıldığı iş parçacığını durdurmak dışında iki önemli senaryo:

  1. Çift kontrollu kilitleme mekanizması . Singleton tasarım deseninde sıklıkla kullanılır. Burada singleton nesnesinin geçici olarak ilan edilmesi gerekir .
  2. Sahte Uyandırmalar . Bildirim bildirimi yapılmasa bile, iş parçacığı bazen bekleme çağrısından uyanabilir. Bu davranışa sahte uyandırma denir. Bu koşullu bir değişken (boole bayrağı) kullanılarak sayılabilir. İşaret doğru olduğu sürece wait () çağrısını while döngüsüne koyun. Bu nedenle, Notify / NotifyAll dışında herhangi bir nedenden dolayı iş parçacığı bekleme çağrısından uyanırsa, o zaman bayrakla karşılaşma hala doğrudur ve bu nedenle aramalar tekrar bekler. Bildirim çağırmadan önce bu bayrağı true olarak ayarlayın. Bu durumda, boole bayrağı geçici olarak bildirilir .

Tüm # 2 bölümü çok karışık görünüyor, kayıp bildirimleri, sahte uyandırma ve bellek görünürlüğü sorunlarını karıştırıyor. Ayrıca bayrağın tüm kullanımları senkronize edilmişse, uçucu fazlalık olur. Sanırım ne demek istediğimi anlıyorum ama sahte uyandırma doğru terim değil. Lütfen açıkla.
Nathan Hughes

5

Çok iş parçacıklı bir uygulama geliştiriyorsanız 'geçici' anahtar kelime veya 'senkronize' ve diğer eşzamanlılık kontrol araçlarını ve tekniklerini kullanmanız gerekir. Bu uygulamaya örnek olarak masaüstü uygulamaları verilebilir.

Uygulama sunucusuna (Tomcat, JBoss AS, Glassfish vb.) Dağıtılacak bir uygulama geliştiriyorsanız, eşzamanlılık denetimini uygulama sunucusu tarafından zaten ele alındığı gibi kendiniz yürütmeniz gerekmez. Aslında, Java EE standardını doğru bir şekilde hatırlarsam, sunucu uygulamalarında ve EJB'lerde herhangi bir eşzamanlılık denetimini yasaklar, çünkü onu işlemek için serbest bırakmanız gereken 'altyapı' katmanının bir parçasıdır. Bu tür bir uygulamada eşzamanlılık kontrolü yalnızca singleton nesneleri uyguluyorsanız yapılır. Bu, bileşenlerinizi Spring gibi frameworkd kullanarak ördüyseniz bile ele alındı.

Bu nedenle, uygulamanın bir web uygulaması olduğu ve Spring veya EJB gibi IoC çerçevesini kullanan çoğu Java geliştirme durumunda, 'geçici' kullanmanız gerekmez.


5

volatilesadece tüm ipliklerin, hatta kendilerinin bile artmakta olduğunu garanti eder. Örneğin: bir sayaç, değişkenin aynı yüzünü aynı anda görür. Senkronize veya atomik veya başka şeyler yerine kullanılmaz, okumaları tamamen senkronize eder. Lütfen diğer java anahtar kelimeleriyle karşılaştırmayın. Örnekte aşağıda gösterildiği gibi, değişken değişken işlemler de atomiktir ve bir kerede başarısız olur veya başarılı olurlar.

package io.netty.example.telnet;

import java.util.ArrayList;
import java.util.List;

public class Main {

    public static volatile  int a = 0;
    public static void main(String args[]) throws InterruptedException{

        List<Thread> list = new  ArrayList<Thread>();
        for(int i = 0 ; i<11 ;i++){
            list.add(new Pojo());
        }

        for (Thread thread : list) {
            thread.start();
        }

        Thread.sleep(20000);
        System.out.println(a);
    }
}
class Pojo extends Thread{
    int a = 10001;
    public void run() {
        while(a-->0){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Main.a++;
            System.out.println("a = "+Main.a);
        }
    }
}

Uçucu koyarsanız da koymazsanız bile sonuçlar her zaman farklı olacaktır. Ancak AtomicInteger'ı aşağıdaki gibi kullanırsanız sonuçlar her zaman aynı olacaktır. Bu senkronize ile de aynıdır.

    package io.netty.example.telnet;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;

    public class Main {

        public static volatile  AtomicInteger a = new AtomicInteger(0);
        public static void main(String args[]) throws InterruptedException{

            List<Thread> list = new  ArrayList<Thread>();
            for(int i = 0 ; i<11 ;i++){
                list.add(new Pojo());
            }

            for (Thread thread : list) {
                thread.start();
            }

            Thread.sleep(20000);
            System.out.println(a.get());

        }
    }
    class Pojo extends Thread{
        int a = 10001;
        public void run() {
            while(a-->0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Main.a.incrementAndGet();
                System.out.println("a = "+Main.a);
            }
        }
    }

4

Evet, çok kullanıyorum - çok iş parçacıklı kod için çok yararlı olabilir. İşaret ettiğin makale iyi. Akılda tutulması gereken iki önemli şey olmasına rağmen:

  1. Uçucuyu yalnızca ne yaptığını ve senkronizasyondan nasıl farklı olduğunu tamamen anlarsanız kullanmalısınız. Çoğu durumda, uçucu, yüzeyde, senkronize için daha basit ve daha performanslı bir alternatif gibi görünür, genellikle uçucunun daha iyi anlaşılması, senkronizasyonun işe yarayacak tek seçenek olduğunu açıkça ortaya koyacaktır.
  2. senkronize olmasına rağmen volatile aslında çok eski JVM'lerde çalışmaz. Farklı JVM'lerde çeşitli destek düzeylerine başvuran bir belge gördüğümü hatırlıyorum ama maalesef şimdi bulamıyorum. Java pre 1.5 kullanıyorsanız veya programınızın çalışacağı JVM'ler üzerinde kontrolünüz yoksa kesinlikle göz önünde bulundurun.

4

Uçucu bir alana erişen her iş parçacığı, önbelleğe alınmış bir değer kullanmak yerine (potansiyel olarak) devam etmeden önce geçerli değerini okuyacaktır.

Yalnızca üye değişkeni geçici veya geçici olabilir.


3

Kesinlikle evet. (Ve sadece Java'da değil, aynı zamanda C #'da da.) Verilen platformunuzda bir atomik işlem olduğu garanti edilen bir değeri almanız veya ayarlamanız gereken, örneğin bir int veya boolean, ancak gerektirmeyen zamanlar vardır. iplik kilidinin üst kısmı. Uçucu anahtar kelime, değeri okuduğunuzda , yalnızca başka bir iş parçacığındaki bir yazma tarafından geçersiz kılınan önbelleğe alınmış bir değeri değil , geçerli değeri aldığınızdan emin olmanızı sağlar .


3

Uçucu anahtar kelimenin iki farklı kullanımı vardır.

  1. JVM'nin değerleri kayıttan okumasını önler (önbellek olarak varsayın) ve değerini bellekten okunmaya zorlar.
  2. Bellek tutarsızlığı hatası riskini azaltır.

JVM'nin kayıttaki değerleri okumasını engeller ve değerini bellekten okunmaya zorlar.

Bir meşgul bayrak cihaz meşgul ve bayrak bir kilit tarafından korunmayan iken devam etmesini bir iş parçacığı önlemek için kullanılır:

while (busy) {
    /* do something else */
}

Başka bir iş parçacığı meşgul bayrağını kapattığında test iş parçacığı devam eder :

busy = 0;

Bununla birlikte, test iş parçacığında meşgul'e sık erişildiğinden, JVM meşgul değerini bir kayıt defterine yerleştirerek testi optimize edebilir, ardından her testten önce bellekte meşgul değerini okumadan kayıt içeriğini test edebilir. Test iş parçacığı asla meşgul değişikliği görmez ve diğer iş parçacığı yalnızca bellekte meşgul değerini değiştirir ve kilitlenmeye neden olur. Meşgul bayrağını geçici olarak bildirmek, değerini her testten önce okunmaya zorlar.

Bellek tutarlılığı hatası riskini azaltır.

Uçucu değişkenlerin kullanılması bellek tutarlılığı hataları riskini azaltır , çünkü uçucu bir değişkene herhangi bir yazma işlemi "daha önce olur" , aynı değişkenin sonraki okumalarıyla daha ilişkisi kurar. Bu, değişken bir değişkende yapılan değişikliklerin her zaman diğer iş parçacıkları tarafından görülebilir olduğu anlamına gelir.

Bellek tutarlılık hatası olmadan okuma, yazma tekniğine atomik eylem denir .

Bir atomik eylem, aynı anda etkili bir şekilde gerçekleşen eylemdir. Atomik eylem ortada duramaz: ya tamamen olur ya da hiç olmaz. Eylem tamamlanana kadar atomik bir eylemin hiçbir yan etkisi görülmez.

Atomik olduğunu belirtebileceğiniz eylemler aşağıdadır:

  • Okuma ve yazma işlemleri referans değişkenleri ve çoğu ilkel değişken için atomiktir (uzun ve çift hariç tüm tipler).
  • Okuma ve yazma işlemleri uçucu olarak bildirilen tüm değişkenler için atomiktir (uzun ve çift değişkenler dahil).

Şerefe!


3

volatilebir programcı için değerin her zaman güncel olacağını söylüyor. Sorun, değerin farklı donanım belleği türlerine kaydedilebilmesidir. Örneğin, CPU kayıtları, CPU önbelleği, RAM olabilir ... СPU kayıtları ve CPU önbelleği CPU'ya aittir ve çok iş parçacıklı ortamda kurtarma işleminde bulunan RAM'in aksine bir veri paylaşamaz

resim açıklamasını buraya girin

volatileanahtar kelime, bir değişkenin doğrudan RAM belleğe / belleğinden okunup yazılacağını söylüyor . Bazı hesaplama ayak izi var

Java 5volatiledestekleyerek genişletildi happens-before[Hakkında]

Uçucu bir alana bir yazma gerçekleşir - bu alanın sonraki her okunmasından önce.

volatileanahtar kelime ,race condition birkaç iş parçacığının aynı anda bazı değerleri yazabileceği bir durumu iyileştirmez . Cevap synchronizedanahtar kelime [Hakkında]

Sonuç olarak, sadece bir iş parçacığı yazarken ve diğerleri sadecevolatile değeri

uçucu vs senkronize


2

Uçucu takip eder.

1> Farklı iş parçacıkları tarafından uçucu değişkenlerin okunması ve yazılması, iş parçacığının kendi önbelleğinden veya işlemci kaydından değil, daima bellektir. Böylece her iş parçacığı her zaman en son değerle ilgilenir. 2> Öbekte aynı örnek veya statik değişkenlerle 2 farklı iş parçacığı çalıştığında, diğerinin eylemleri bozuk olarak görülebilir. Bu konuda jeremy manson bloguna bakın. Ama burada uçucu yardımcı olur.

Tamamen çalışan kodun izlenmesi, bir dizi iş parçacığının, önceden belirlenmiş sırayla ve baskı çıktılarını senkronize edilmiş anahtar kelime kullanmadan nasıl çalıştırabileceğini gösterir.

thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3

Bunu başarmak için aşağıdaki tam teşekküllü koşu kodunu kullanabiliriz.

public class Solution {
    static volatile int counter = 0;
    static int print = 0;
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Thread[] ths = new Thread[4];
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new Thread(new MyRunnable(i, ths.length));
            ths[i].start();
        }
    }
    static class MyRunnable implements Runnable {
        final int thID;
        final int total;
        public MyRunnable(int id, int total) {
            thID = id;
            this.total = total;
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (true) {
                if (thID == counter) {
                    System.out.println("thread " + thID + " prints " + print);
                    print++;
                    if (print == total)
                        print = 0;
                    counter++;
                    if (counter == total)
                        counter = 0;
                } else {
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        // log it
                    }
                }
            }
        }
    }
}

Aşağıdaki github bağlantısının doğru açıklaması veren bir benioku vardır. https://github.com/sankar4git/volatile_thread_ordering


1

Oracle dokümantasyon sayfasından , bellek tutarlılığı sorunlarını gidermek için değişken değişken ihtiyacı ortaya çıkar:

Uçucu değişkenlerin kullanılması, bellek tutarlılığı hataları riskini azaltır, çünkü uçucu bir değişkene herhangi bir yazma işlemi, aynı değişkenin sonraki okumalarıyla önceden gerçekleşen bir ilişki kurar.

Bu, bir volatiledeğişkende yapılan değişikliklerin her zaman diğer iş parçacıkları tarafından görülebilir olduğu anlamına gelir . Ayrıca, bir iş parçacığı değişken bir değişkeni okuduğunda, yalnızca en son değişikliği değil volatile, aynı zamanda değişikliğe yol açan kodun yan etkilerini de görür .

Yanıtta açıklandığı gibi Peter Parker, volatiledeğiştiricinin yokluğunda , her bir iş parçacığının yığını kendi değişken kopyasına sahip olabilir. Değişkeni şu şekilde yaparak volatilebellek tutarlılığı sorunları giderildi.

Göz at jenkov daha iyi anlaşılması için öğretici sayfa.

Uçucu ve uçucu kullanmak için kullanım örnekleri hakkında daha fazla ayrıntı için ilgili SE sorusuna bir göz atın:

Java'da uçucu ve senkronize arasındaki fark

Bir pratik kullanım örneği:

Örneğin, belirli bir formatta geçerli saati yazdırmak gerekiyor birçok konuları vardır: java.text.SimpleDateFormat("HH-mm-ss"). Yon, geçerli saati SimpleDateFormather saniyede bir değişkene dönüştüren ve güncelleyen bir sınıfa sahip olabilir . Diğer tüm evreler, günlük dosyalarında geçerli saati yazdırmak için bu değişken değişkeni kullanabilir.


1

Uçucu Değişkenler hafif senkronizasyondur. Tüm iş parçacıkları arasındaki en son verilerin görünürlüğü zorunlu olduğunda ve atomisiteden ödün verilebildiğinde, bu gibi durumlarda Değişken Değişkenler tercih edilmelidir. Uçucu değişkenler üzerindeki okumalar, ne kayıtlarda ne de diğer işlemcilerin göremediği önbelleklerde önbelleğe alınmadığından, herhangi bir iş parçacığı tarafından yapılan en son yazmayı her zaman döndürür. Uçucu Kilitsizdir. Senaryo yukarıda belirtilen kriterleri karşıladığında geçici olarak kullanıyorum.


-1

Bir değişkenle birlikte kullanıldığında geçici anahtar, bu değişkeni okuyan iş parçacıklarının aynı değeri görmesini sağlar. Bir değişkene okuma ve yazma için birden fazla iş parçacığınız varsa, değişkenin değişken hale getirilmesi yeterli olmayacak ve veriler bozulacaktır. Görüntü iş parçacıkları aynı değeri okudu, ancak her biri bazı sayılar yaptı (bir sayacı artırdı), belleğe geri yazarken veri bütünlüğü ihlal edildi. Bu yüzden değişken senkronizasyonu yapmak gerekir (farklı yollar mümkündür)

Değişiklikler 1 iş parçacığı tarafından yapılırsa ve diğerlerinin bu değeri okuması gerekiyorsa, uçucu uygun olacaktır.


-1

uçucu değişken temel olarak ana paylaşımlı önbellek satırında güncellendikten sonra anında güncelleme (yıkama) için kullanılır, böylece değişiklikler tüm çalışan iş parçacıklarına hemen yansır.


-2

Aşağıda, volatileiş parçacığının diğer iş parçacığından yürütülmesini denetlemek için kullanılan değişken için gereksinimi göstermek için çok basit bir kod bulunmaktadır (bu volatilegerekli olan bir senaryodur ).

// Code to prove importance of 'volatile' when state of one thread is being mutated from another thread.
// Try running this class with and without 'volatile' for 'state' property of Task class.
public class VolatileTest {
    public static void main(String[] a) throws Exception {
        Task task = new Task();
        new Thread(task).start();

        Thread.sleep(500);
        long stoppedOn = System.nanoTime();

        task.stop(); // -----> do this to stop the thread

        System.out.println("Stopping on: " + stoppedOn);
    }
}

class Task implements Runnable {
    // Try running with and without 'volatile' here
    private volatile boolean state = true;
    private int i = 0;

    public void stop() {
        state = false;
    } 

    @Override
    public void run() {
        while(state) {
            i++;
        }
        System.out.println(i + "> Stopped on: " + System.nanoTime());
    }
}

Ne zaman volatilekullanılmaz: Eğer 'asla görmeyeceksin : xxx Durduruldu bile' sonra mesajın ' üzerine Durdurma: xxx ' ve program çalışmaya devam eder.

Stopping on: 1895303906650500

Ne zaman volatilekullanılır: Eğer 'göreceksiniz : xxx Durduruldu hemen'.

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300

Demo: https://repl.it/repls/SilverAgonizingObjectcode


Downvoter için: Neden downvote açıklamak ister misiniz? Bu doğru değilse, en azından neyin yanlış olduğunu öğreneceğim. Aynı yorumu iki kez ekledim, ancak kimin tekrar tekrar sildiğini bilmiyorum
manikanta

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.