Untuk apa kata kunci volatil berguna

672

Di tempat kerja hari ini, saya menemukan volatilekata kunci di Jawa. Tidak terlalu akrab dengannya, saya menemukan penjelasan ini:

Teori dan praktik Java: Mengelola volatilitas

Mengingat detail di mana artikel itu menjelaskan kata kunci yang dimaksud, apakah Anda pernah menggunakannya atau dapatkah Anda melihat kasus di mana Anda dapat menggunakan kata kunci ini dengan cara yang benar?

Richard
sumber

Jawaban:

742

volatilememiliki semantik untuk visibilitas memori. Pada dasarnya, nilai volatilebidang menjadi terlihat oleh semua pembaca (utas lainnya khususnya) setelah operasi penulisan selesai di atasnya. Tanpa volatile, pembaca dapat melihat beberapa nilai yang tidak diperbarui.

Untuk menjawab pertanyaan Anda: Ya, saya menggunakan volatilevariabel untuk mengontrol apakah beberapa kode melanjutkan loop. Loop menguji volatilenilai dan melanjutkan jika itu true. Kondisi ini dapat diatur falsedengan memanggil metode "stop". Loop melihat falsedan berakhir ketika menguji nilai setelah metode berhenti menyelesaikan eksekusi.

Buku " Java Concurrency in Practice ," yang sangat saya rekomendasikan, memberikan penjelasan yang baik tentang volatile. Buku ini ditulis oleh orang yang sama yang menulis artikel IBM yang dirujuk dalam pertanyaan (pada kenyataannya, ia mengutip bukunya di bagian bawah artikel itu). Penggunaan saya atas volatileapa yang oleh artikelnya disebut "bendera status pola 1".

Jika Anda ingin mempelajari lebih lanjut tentang cara volatilekerjanya di bawah tenda, bacalah tentang model memori Java . Jika Anda ingin melampaui level itu, lihat buku arsitektur komputer yang bagus seperti Hennessy & Patterson dan baca tentang koherensi cache dan konsistensi cache.

Greg Mattes
sumber
118
Jawaban ini benar, tetapi tidak lengkap. Itu menghilangkan properti penting volatileyang datang dengan Java Memory Model baru yang didefinisikan dalam JSR 133: bahwa ketika sebuah thread membaca sebuah volatilevariabel, ia melihat tidak hanya nilai yang terakhir ditulis untuknya oleh beberapa thread lain, tetapi juga semua yang lain menulis ke variabel lain yang terlihat di utas lainnya pada saat volatilepenulisan. Lihat jawaban ini dan referensi ini .
Adam Zalcman
46
Untuk pemula, saya akan meminta Anda untuk menunjukkan dengan beberapa kode (tolong?)
Hungry Blue Dev
6
Artikel yang ditautkan dalam pertanyaan memiliki contoh kode.
Greg Mattes
Saya pikir tautan 'Hennessy & Patterson' rusak. Dan tautan ke 'model memori Java' sebenarnya mengarah ke Spesifikasi Bahasa Jawa Oracle 'Bab 17. Threads and Locks'.
Kris
2
@fefrei: "segera" adalah istilah sehari-hari. Tentu saja, itu tidak dapat dijamin ketika keduanya, waktu eksekusi atau algoritma penjadwalan thread, sebenarnya ditentukan. Satu-satunya cara bagi sebuah program untuk mengetahui apakah pembacaan volatil adalah setelah penulisan volatil tertentu, adalah dengan memeriksa apakah nilai yang terlihat adalah yang ditulis yang diharapkan.
Holger
177

"... pengubah volatil menjamin bahwa utas apa pun yang membaca bidang akan melihat nilai yang paling baru ditulis." - Josh Bloch

Jika Anda berpikir untuk menggunakan volatile, bacalah paket java.util.concurrentyang berkaitan dengan perilaku atom.

Posting Wikipedia pada Pola Singleton menunjukkan volatile digunakan.

Ande TURNER
sumber
18
Mengapa ada kata kunci volatiledan keduanya synchronized?
ptkato
5
Artikel Wikipedia tentang Pola Singleton telah banyak berubah sejak dan tidak menampilkan volatilecontoh kata lebih lama. Itu dapat ditemukan dalam versi yang diarsipkan .
bskp
1
@ptkato Kedua kata kunci tersebut memiliki tujuan yang sangat berbeda, jadi pertanyaannya tidak masuk akal sebagai perbandingan, meskipun keduanya terkait dengan konkurensi. Ini seperti mengatakan "Mengapa ada kata kunci voiddan keduanya public".
DavidS
134

Poin penting tentang volatile:

  1. Sinkronisasi di Jawa dimungkinkan dengan menggunakan kata kunci synchronizeddan volatiledan kunci Java .
  2. Di Jawa, kita tidak bisa memiliki synchronizedvariabel. Menggunakan synchronizedkata kunci dengan variabel adalah ilegal dan akan menghasilkan kesalahan kompilasi. Alih-alih menggunakan synchronizedvariabel di Jawa, Anda bisa menggunakan volatilevariabel java , yang akan menginstruksikan thread JVM untuk membaca nilai volatilevariabel dari memori utama dan jangan cache secara lokal.
  3. Jika suatu variabel tidak dibagi di antara beberapa utas, maka tidak perlu menggunakan volatilekata kunci.

sumber

Contoh penggunaan 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;
    }
}

Kami membuat contoh dengan malas pada saat permintaan pertama datang.

Jika kita tidak membuat _instancevariabel volatilemaka Thread yang membuat instance dari Singletontidak dapat berkomunikasi ke thread lain. Jadi jika Thread A membuat Singleton instance dan setelah penciptaan, CPU rusak dll, semua utas lainnya tidak akan dapat melihat nilai _instancebukan nol dan mereka akan percaya itu masih ditugaskan nol.

Mengapa ini terjadi? Karena utas pembaca tidak melakukan penguncian dan sampai utas penulis keluar dari blok yang disinkronkan, memori tidak akan disinkronkan dan nilai _instancetidak akan diperbarui di memori utama. Dengan kata kunci Volatile di Java, ini ditangani oleh Java sendiri dan pembaruan tersebut akan terlihat oleh semua utas pembaca.

Kesimpulan : volatilekata kunci juga digunakan untuk mengkomunikasikan konten memori antara utas.

Contoh penggunaan tanpa volatile:

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;  
    }

Kode di atas tidak aman untuk thread. Meskipun memeriksa nilai instance sekali lagi di dalam blok yang disinkronkan (untuk alasan kinerja), kompiler JIT dapat mengatur ulang bytecode sedemikian rupa sehingga referensi ke instance diatur sebelum konstruktor menyelesaikan eksekusi. Ini berarti metode getInstance () mengembalikan objek yang mungkin belum diinisialisasi sepenuhnya. Untuk membuat kode-aman, kata kunci volatile dapat digunakan sejak Java 5 untuk variabel instan. Variabel yang ditandai sebagai volatil hanya dapat dilihat oleh utas lainnya setelah konstruktor objek telah menyelesaikan eksekusi sepenuhnya.
Sumber

masukkan deskripsi gambar di sini

volatilepenggunaan di Jawa :

Iterator gagal-cepat biasanya diimplementasikan menggunakan volatilepenghitung pada objek daftar.

  • Ketika daftar diperbarui, penghitung bertambah.
  • Ketika sebuah Iteratordibuat, nilai saat ini dari penghitung tertanam di Iteratorobjek.
  • Ketika Iteratoroperasi dilakukan, metode ini membandingkan dua nilai penghitung dan melempar a ConcurrentModificationExceptionjika mereka berbeda.

Implementasi iterator gagal-aman biasanya ringan. Mereka biasanya mengandalkan properti dari struktur data implementasi daftar tertentu. Tidak ada pola umum.

Premraj
sumber
2
"Iterator gagal-cepat biasanya diimplementasikan menggunakan penghitung volatile" - tidak lagi menjadi masalah, terlalu mahal: bugs.java.com/bugdatabase/view_bug.do?bug_id=6625725
Vsevolod Golovanov
apakah pemeriksaan ganda untuk _instance aman? saya pikir mereka tidak aman bahkan dengan volatile
Dexters
"Yang akan menginstruksikan utas JVM untuk membaca nilai variabel volatil dari memori utama dan jangan menyimpannya secara lokal." Poin bagus
Humoyun Ahmad
Untuk keamanan benang, seseorang dapat menggunakannya private static final Singleton _instance;juga.
Chris311
53

volatile sangat berguna untuk menghentikan utas.

Bukan berarti Anda harus menulis utas sendiri, Java 1.6 memiliki banyak kumpulan utas yang bagus. Tetapi jika Anda yakin membutuhkan utas, Anda harus tahu cara menghentikannya.

Pola yang saya gunakan untuk utas adalah:

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
  }
}

Di segmen kode di atas, utas pembacaan closedi loop sementara berbeda dari yang memanggil close(). Tanpa volatile, utas yang menjalankan loop mungkin tidak akan pernah melihat perubahan ditutup.

Perhatikan bagaimana tidak perlu sinkronisasi

Pirolistik
sumber
2
Saya bertanya-tanya mengapa itu bahkan perlu. Bukankah itu hanya perlu jika utas lain harus bereaksi terhadap perubahan status utas ini sedemikian rupa sehingga sinkronisasi utas dalam bahaya?
Jori
27
@ Jori, Anda perlu volatile karena utas membaca tutup di loop sementara berbeda dari yang memanggil tutup (). Tanpa volatile, utas yang menjalankan loop mungkin tidak akan pernah melihat perubahan ditutup.
Pirolistik
akankah Anda mengatakan ada keuntungan antara menghentikan thread seperti itu atau menggunakan metode Thread # interrupt () dan Thread # isInterrupted ()?
Ricardo Belchior
2
@ Patrolistik - Pernahkah Anda mengamati utas yang tidak pernah melihat perubahan dalam praktik? Atau bisakah Anda memberikan contoh untuk memicu masalah itu secara andal? Saya ingin tahu karena saya tahu saya telah menggunakan (dan melihat orang lain menggunakan) kode yang pada dasarnya identik dengan contoh tetapi tanpa volatilekata kunci, dan sepertinya selalu berfungsi dengan baik.
Agustus
2
@aroth: dengan JVM hari ini, Anda dapat mengamati bahwa dalam praktiknya, bahkan dengan contoh-contoh paling sederhana, Anda tidak dapat mereproduksi perilaku ini dengan andal . Dengan aplikasi yang lebih kompleks, Anda kadang-kadang memiliki tindakan lain dengan jaminan visibilitas memori di dalam kode Anda yang membuatnya bekerja, yang sangat berbahaya karena Anda tidak tahu mengapa ia bekerja dan perubahan kode Anda yang sederhana dan tampaknya tidak terkait dapat merusak kode Anda. aplikasi ...
Holger
31

Salah satu contoh umum untuk menggunakan volatileadalah menggunakan volatile booleanvariabel sebagai bendera untuk mengakhiri utas. Jika Anda telah memulai utas, dan Anda ingin dapat dengan aman menghentikannya dari utas lain, Anda dapat meminta utas secara berkala memeriksa bendera. Untuk menghentikannya, atur bendera ke true. Dengan membuat bendera volatile, Anda dapat memastikan bahwa utas yang memeriksanya akan melihatnya telah disetel saat berikutnya memeriksa tanpa harus menggunakan synchronizedblok.

Dave L.
sumber
27

Variabel yang dideklarasikan dengan volatilekata kunci, memiliki dua kualitas utama yang membuatnya istimewa.

  1. Jika kita memiliki variabel volatil, itu tidak bisa di-cache ke memori cache (mikroprosesor) komputer dengan utas apa pun. Akses selalu terjadi dari memori utama.

  2. Jika ada operasi tulis yang sedang berjalan pada variabel yang tidak stabil, dan tiba-tiba operasi baca diminta, dijamin operasi penulisan akan selesai sebelum operasi baca .

Dua kualitas di atas menyimpulkan itu

  • Semua utas yang membaca variabel volatil pasti akan membaca nilai terbaru. Karena tidak ada nilai yang di-cache dapat mencemari itu. Dan juga permintaan baca akan diberikan hanya setelah selesainya operasi penulisan saat ini.

Dan di sisi lain,

  • Jika kita menyelidiki lebih lanjut # 2 yang telah saya sebutkan, kita dapat melihat bahwa volatilekata kunci adalah cara yang ideal untuk mempertahankan variabel bersama yang memiliki 'n' jumlah utas pembaca dan hanya satu utas penulis untuk mengaksesnya. Setelah kami menambahkan volatilekata kunci, itu selesai. Tidak ada overhead lain tentang keamanan ulir.

Sebaliknya

Kami tidak dapat menggunakan volatilekata kunci semata-mata, untuk memenuhi variabel bersama yang memiliki lebih dari satu penulis yang mengaksesnya .

Supun Wijerathne
sumber
3
Ini menjelaskan perbedaan antara volatile dan yang disinkronkan.
ajay
13

Tidak ada yang menyebutkan perlakuan operasi baca dan tulis untuk tipe variabel panjang dan ganda. Baca dan tulis adalah operasi atom untuk variabel referensi dan untuk sebagian besar variabel primitif, kecuali untuk tipe variabel panjang dan ganda, yang harus menggunakan kata kunci yang mudah menguap untuk menjadi operasi atom. @tautan

Donatello Boccaforno
sumber
Untuk membuatnya lebih jelas, TIDAK perlu untuk mengatur volatile boolean, karena membaca dan menulis boolean ADALAH SUDAH atom.
Kai Wang
2
@ KaiWang Anda tidak perlu menggunakan volatile di boolean untuk keperluan atomicity. Tapi Anda tentu bisa karena alasan visibilitas. Apakah itu yang ingin Anda katakan?
SusanW
12

Ya, volatile harus digunakan kapan pun Anda ingin variabel yang dapat diubah diakses oleh banyak utas. Ini bukan penggunaan umum yang sangat umum karena biasanya Anda perlu melakukan lebih dari satu operasi atom (misalnya memeriksa keadaan variabel sebelum memodifikasinya), dalam hal ini Anda akan menggunakan blok yang disinkronkan sebagai gantinya.

ykaganovich
sumber
10

Menurut pendapat saya, dua skenario penting selain menghentikan utas yang digunakan kata kunci yang mudah menguap adalah:

  1. Mekanisme penguncian diperiksa dua kali . Sering digunakan dalam pola desain Singleton. Dalam hal ini objek singleton perlu dinyatakan volatile .
  2. Wakeup palsu . Utas terkadang bangun dari panggilan tunggu meskipun tidak ada pemberitahuan yang dikeluarkan. Perilaku ini disebut bangun palsu. Ini dapat dilawan dengan menggunakan variabel kondisional (bendera boolean). Masukkan panggilan wait () dalam loop sementara selama flag itu benar. Jadi jika utas bangun dari panggilan tunggu karena alasan apa pun selain dari Beri tahu / Beri TahuSemua maka yang ditemuinya bendera masih benar dan karenanya panggilan tunggu lagi. Sebelum menelepon, beri tahu flag ini menjadi true. Dalam hal ini bendera boolean dinyatakan sebagai volatile .
Aniket Thakur
sumber
Bagian # 2 keseluruhan tampaknya sangat bingung, itu menggabungkan notifikasi yang hilang, wakeups palsu, dan masalah visibilitas memori. Juga jika semua penggunaan bendera disinkronkan, maka volatile adalah redundan. Saya pikir saya mengerti maksud Anda tetapi bangun palsu bukan istilah yang tepat. Mohon klarifikasi.
Nathan Hughes
5

Anda harus menggunakan kata kunci 'mudah menguap', atau 'disinkronkan' dan alat serta teknik kontrol konkurensi lain yang mungkin Anda miliki jika Anda mengembangkan aplikasi multithreaded. Contoh aplikasi tersebut adalah aplikasi desktop.

Jika Anda mengembangkan aplikasi yang akan digunakan untuk server aplikasi (Tomcat, JBoss AS, Glassfish, dll) Anda tidak perlu menangani kontrol konkurensi sendiri karena sudah ditangani oleh server aplikasi. Bahkan, jika saya ingat dengan benar standar Java EE melarang kontrol konkurensi dalam servlets dan EJB, karena itu adalah bagian dari lapisan 'infrastruktur' yang seharusnya Anda bebas dari penanganannya. Anda hanya melakukan kontrol konkurensi dalam aplikasi tersebut jika Anda menerapkan objek tunggal. Ini bahkan sudah diatasi jika Anda merajut komponen Anda menggunakan frameworkd seperti Spring.

Jadi, dalam kebanyakan kasus pengembangan Java di mana aplikasi adalah aplikasi web dan menggunakan kerangka kerja IoC seperti Spring atau EJB, Anda tidak perlu menggunakan 'volatile'.

Rudi Adianto
sumber
5

volatilehanya menjamin bahwa semua utas, bahkan sendiri, bertambah. Sebagai contoh: penghitung melihat wajah variabel yang sama pada saat yang sama. Itu tidak digunakan bukannya disinkronkan atau atom atau hal-hal lain, itu benar-benar membuat pembacaan disinkronkan. Tolong jangan membandingkannya dengan kata kunci java lainnya. Seperti contoh yang ditunjukkan di bawah ini, operasi variabel yang tidak tetap juga bersifat atomis, mereka gagal atau berhasil sekaligus.

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);
        }
    }
}

Bahkan Anda menempatkan volatile atau tidak, hasilnya akan selalu berbeda. Tetapi jika Anda menggunakan AtomicInteger seperti di bawah ini hasilnya akan selalu sama. Ini sama dengan disinkronkan juga.

    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);
            }
        }
    }
fatih tekin
sumber
4

Ya, saya menggunakannya cukup banyak - ini bisa sangat berguna untuk kode multi-threaded. Artikel yang Anda tunjuk adalah yang bagus. Padahal ada dua hal penting yang harus diingat:

  1. Anda hanya boleh menggunakan volatile jika Anda benar-benar memahami apa yang dilakukannya dan bagaimana perbedaannya untuk disinkronkan. Dalam banyak situasi, volatile muncul, di permukaan, menjadi alternatif yang lebih sederhana untuk disinkronkan, ketika seringkali pemahaman yang lebih baik tentang volatile akan memperjelas bahwa sinkronisasi adalah satu-satunya pilihan yang akan bekerja.
  2. volatile tidak benar-benar berfungsi di banyak JVM lama, meskipun disinkronisasi tidak. Saya ingat melihat dokumen yang mereferensikan berbagai tingkat dukungan di JVM yang berbeda tetapi sayangnya saya tidak dapat menemukannya sekarang. Pasti melihat ke dalamnya jika Anda menggunakan Java pre 1.5 atau jika Anda tidak memiliki kendali atas JVMs bahwa program Anda akan berjalan.
MB.
sumber
4

Setiap utas yang mengakses bidang volatil akan membaca nilai saat ini sebelum melanjutkan, alih-alih (berpotensi) menggunakan nilai yang di-cache.

Hanya variabel anggota yang dapat berubah-ubah atau sementara.

tstuber
sumber
3

Pastinya ya. (Dan tidak hanya di Jawa, tetapi juga di C #.) Ada kalanya Anda perlu mendapatkan atau menetapkan nilai yang dijamin menjadi operasi atom pada platform Anda, int atau boolean, misalnya, tetapi tidak memerlukan overhead dari penguncian utas. Kata kunci yang mudah menguap memungkinkan Anda untuk memastikan bahwa ketika Anda membaca nilai bahwa Anda mendapatkan nilai saat ini dan bukan nilai yang di-cache yang baru saja dibuat usang oleh tulisan di utas lainnya.

dgvid
sumber
3

Ada dua penggunaan kata kunci volatile yang berbeda.

  1. Mencegah JVM dari membaca nilai dari register (menganggap sebagai cache), dan memaksa nilainya untuk dibaca dari memori.
  2. Mengurangi risiko kesalahan konsistensi memori.

Mencegah JVM dari membaca nilai dalam register, dan memaksa nilainya untuk dibaca dari memori.

Sebuah bendera sibuk digunakan untuk mencegah thread dari melanjutkan saat perangkat sedang sibuk dan bendera tidak dilindungi oleh kunci:

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

Utas pengujian akan dilanjutkan ketika utas lain mematikan bendera sibuk :

busy = 0;

Namun, karena sibuk sering diakses di utas pengujian, JVM dapat mengoptimalkan tes dengan menempatkan nilai sibuk dalam register, kemudian menguji konten register tanpa membaca nilai sibuk dalam memori sebelum setiap tes. Utas pengujian tidak akan pernah melihat perubahan yang sibuk dan utas lainnya hanya akan mengubah nilai sibuk di memori, menghasilkan jalan buntu. Mendeklarasikan flag sibuk sebagai volatile memaksa nilainya untuk dibaca sebelum setiap tes.

Mengurangi risiko kesalahan konsistensi memori.

Menggunakan variabel volatile mengurangi risiko kesalahan konsistensi memori , karena setiap penulisan ke variabel volatile membentuk hubungan "terjadi sebelum" dengan pembacaan selanjutnya dari variabel yang sama. Ini berarti bahwa perubahan ke variabel volatil selalu terlihat oleh utas lainnya.

Teknik membaca, menulis tanpa kesalahan konsistensi memori disebut aksi atom .

Tindakan atom adalah tindakan yang secara efektif terjadi sekaligus. Tindakan atom tidak bisa berhenti di tengah: itu bisa terjadi sepenuhnya, atau tidak terjadi sama sekali. Tidak ada efek samping dari aksi atom yang terlihat sampai aksi selesai.

Di bawah ini adalah tindakan yang dapat Anda tentukan yang bersifat atom:

  • Baca dan tulis adalah atom untuk variabel referensi dan untuk sebagian besar variabel primitif (semua tipe kecuali panjang dan ganda).
  • Baca dan tulis adalah atom untuk semua variabel yang dinyatakan volatil (termasuk variabel panjang dan ganda).

Bersulang!

Mohanraj Balasubramaniam
sumber
3

volatilemengatakan untuk seorang programmer bahwa nilainya selalu up to date. Masalahnya adalah bahwa nilainya dapat disimpan pada berbagai jenis memori perangkat keras. Misalnya bisa register CPU, cache CPU, RAM ... register СPU dan cache CPU milik CPU dan tidak dapat berbagi data tidak seperti RAM yang ada di penyelamatan dalam multithreading envirompment

masukkan deskripsi gambar di sini

volatilekata kunci mengatakan bahwa suatu variabel akan dibaca dan ditulis dari / ke memori RAM secara langsung . Ini memiliki beberapa jejak perhitungan

Java 5diperpanjang volatiledengan mendukung happens-before[Tentang]

Tulisan ke bidang volatil terjadi sebelum setiap pembacaan berikutnya bidang itu.

volatilekata kunci tidak menyembuhkan suatu race conditionsituasi ketika beberapa thread dapat menulis beberapa nilai secara bersamaan. Jawabannya adalah synchronizedkata kunci [Tentang]

Akibatnya itu aman hanya ketika satu utas menulis dan lain-lain hanya membaca volatilenilai

volatile vs disinkronkan

yoAlex5
sumber
2

Volatile mengikuti.

1> Baca dan tulis variabel volatil dengan utas berbeda selalu dari memori, bukan dari cache utas atau cpu register. Jadi setiap utas selalu berurusan dengan nilai terbaru. 2> Ketika 2 utas berbeda bekerja dengan instance atau variabel statis yang sama di heap, orang mungkin melihat tindakan orang lain tidak sesuai pesanan. Lihat blog jeremy manson di ini. Tetapi volatile membantu di sini.

Mengikuti kode yang berjalan sepenuhnya menunjukkan bagaimana sejumlah utas dapat mengeksekusi dalam urutan yang telah ditentukan dan mencetak hasil tanpa menggunakan kata kunci yang disinkronkan.

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

Untuk mencapai ini, kita dapat menggunakan kode berjalan lengkap berikut ini.

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
                    }
                }
            }
        }
    }
}

Tautan github berikut memiliki readme, yang memberikan penjelasan yang tepat. https://github.com/sankar4git/volatile_thread_ordering

sankar banerjee
sumber
1

Dari halaman dokumentasi oracle , kebutuhan untuk variabel volatile muncul untuk memperbaiki masalah konsistensi memori:

Menggunakan variabel volatile mengurangi risiko kesalahan konsistensi memori, karena setiap penulisan ke variabel volatile menetapkan hubungan yang terjadi sebelum dengan membaca selanjutnya dari variabel yang sama.

Ini berarti bahwa perubahan pada suatu volatilevariabel selalu terlihat oleh utas lainnya. Ini juga berarti bahwa ketika utas membaca variabel yang tidak stabil, ia melihat tidak hanya perubahan terbaru ke volatile, tetapi juga efek samping dari kode yang menyebabkan perubahan.

Seperti yang dijelaskan dalam Peter Parkerjawaban, tanpa adanya volatilepengubah, setiap tumpukan thread dapat memiliki salinan variabel mereka sendiri. Dengan membuat variabel sebagai volatile, masalah konsistensi memori telah diperbaiki.

Lihat halaman tutorial jenkov untuk pemahaman yang lebih baik.

Lihat pertanyaan SE terkait untuk beberapa detail lebih lanjut tentang volatile & use cases untuk menggunakan volatile:

Perbedaan antara volatile dan disinkronkan di Jawa

Satu kasus penggunaan praktis:

Anda memiliki banyak benang, yang perlu untuk mencetak waktu saat ini dalam format tertentu misalnya: java.text.SimpleDateFormat("HH-mm-ss"). Yon dapat memiliki satu kelas, yang mengubah waktu saat ini menjadi SimpleDateFormatdan memperbarui variabel untuk setiap satu detik. Semua utas lainnya dapat menggunakan variabel volatil ini untuk mencetak waktu saat ini dalam file log.

Ravindra babu
sumber
1

Variabel Volatile adalah sinkronisasi ringan. Ketika visibilitas data terbaru di antara semua utas adalah persyaratan dan atomisitas dapat dikompromikan, dalam situasi seperti itu, Variabel Volatil harus lebih disukai. Baca pada variabel yang mudah menguap selalu mengembalikan penulisan terbaru yang dilakukan oleh utas apa pun karena mereka tidak di-cache dalam register atau dalam cache di mana prosesor lain tidak dapat melihat. Volatile Bebas Kunci. Saya menggunakan volatile, ketika skenario memenuhi kriteria seperti yang disebutkan di atas.

Neha Vari
sumber
-1

Tombol volatil saat digunakan dengan variabel, akan memastikan bahwa utas yang membaca variabel ini akan melihat nilai yang sama. Sekarang jika Anda memiliki banyak utas yang membaca dan menulis ke suatu variabel, membuat variabel volatile tidak akan cukup dan data akan rusak. Utas gambar telah membaca nilai yang sama tetapi masing-masing telah melakukan beberapa chages (katakanlah menambahkan penghitung), ketika menulis kembali ke memori, integritas data dilanggar. Itu sebabnya perlu untuk membuat variabel disinkronkan (cara yang berbeda dimungkinkan)

Jika perubahan dilakukan oleh 1 utas dan yang lainnya hanya perlu membaca nilai ini, volatile akan cocok.

Java Main
sumber
-1

variabel volatile pada dasarnya digunakan untuk pembaruan instan (flush) di baris cache bersama utama setelah diperbarui, sehingga perubahan tercermin ke semua utas pekerja segera.

Niyaz Ahamad
sumber
-2

Di bawah ini adalah kode yang sangat sederhana untuk menunjukkan persyaratan volatileuntuk variabel yang digunakan untuk mengontrol eksekusi Thread dari utas lainnya (ini adalah satu skenario di mana volatilediperlukan).

// 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());
    }
}

Ketika volatiletidak digunakan: Anda tidak akan pernah melihat pesan ' Berhenti di: xxx ' bahkan setelah ' Berhenti di: xxx ', dan program terus berjalan.

Stopping on: 1895303906650500

Saat volatiledigunakan: Anda akan segera melihat ' Berhenti di: xxx '.

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300

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

manikanta
sumber
Untuk downvoter: Ingin menjelaskan mengapa downvote? Jika ini tidak benar, setidaknya saya akan belajar apa yang salah. Saya telah menambahkan komentar yang sama ini dua kali, tetapi tidak tahu siapa yang menghapus lagi dan lagi
manikanta