it-swarm-id.com

Mengapa volatile digunakan dalam penguncian ganda diperiksa

Dari Kepala Pertama buku pola desain, pola tunggal dengan penguncian ganda diperiksa telah diimplementasikan sebagai berikut: 

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

Saya tidak mengerti mengapa volatile digunakan. Tidakkah penggunaan volatile mengalahkan tujuan penggunaan kinerja penguncian beranda ganda?

59
toc777

Sumber yang bagus untuk memahami mengapa volatile dibutuhkan berasal dari JCIP book. Wikipedia memiliki penjelasan yang layak dari materi itu juga.

Masalah sebenarnya adalah Thread A dapat menetapkan ruang memori untuk instance sebelum selesai membuat instance. Thread B akan melihat tugas itu dan mencoba menggunakannya. Ini mengakibatkan Thread B gagal karena menggunakan versi instance yang dikonstruksi sebagian.

58
Tim Bender

Seperti dikutip oleh @irreputable, volatile tidak mahal. Meskipun mahal, konsistensi harus diprioritaskan daripada kinerja. 

Ada satu cara lagi yang elegan dan bersih untuk Lazy Singletons.

public final class Singleton {
    private Singleton() {}
    public static Singleton getInstance() {
        return LazyHolder.INSTANCE;
    }
    private static class LazyHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

Sumber artikel: Inisialisasi-on-demand_holder_idiom dari wikipedia

Dalam rekayasa perangkat lunak, idiom Inisialisasi pada Pemegang Permintaan (pola desain) adalah singleton yang bermuatan malas. Di semua versi Java, idiom ini memungkinkan inisialisasi malas yang sangat bersamaan dengan kinerja yang baik

Karena kelas tidak memiliki variabel static untuk diinisialisasi, inisialisasi selesai sepele. 

Definisi kelas statis LazyHolder di dalamnya tidak diinisialisasi hingga JVM menentukan bahwa LazyHolder harus dijalankan.

Kelas statis LazyHolder hanya dieksekusi ketika metode statis getInstance dipanggil pada kelas Singleton, dan pertama kali ini terjadi, JVM akan memuat dan menginisialisasi kelas LazyHolder.

Solusi ini aman untuk thread tanpa memerlukan konstruksi bahasa khusus (mis. volatile atau synchronized).

14
Ravindra babu

Jika Anda tidak memilikinya, utas kedua dapat masuk ke blok yang disinkronkan setelah set pertama ke nol, dan cache lokal Anda masih akan berpikir itu nol.

Yang pertama bukan untuk kebenaran (jika Anda benar bahwa itu akan mengalahkan diri sendiri) tetapi untuk optimasi.

1
corsiKa

Bacaan yang tidak stabil tidak terlalu mahal. 

Anda dapat merancang tes untuk memanggil getInstance() dalam loop yang ketat, untuk mengamati dampak dari pembacaan yang tidak stabil; namun tes itu tidak realistis; dalam situasi seperti itu, programmer biasanya akan memanggil getInstance() sekali dan menyimpan instance untuk durasi penggunaan.

Impl lainnya adalah dengan menggunakan bidang final (lihat wikipedia). Ini membutuhkan bacaan tambahan, yang mungkin menjadi lebih mahal daripada versi volatile. Versi final mungkin lebih cepat dalam loop yang ketat, namun tes itu diperdebatkan seperti yang diperdebatkan sebelumnya.

1
irreputable

Mendeklarasikan variabel sebagai volatile menjamin bahwa semua akses ke sana benar-benar membaca nilai saat ini dari memori.

Tanpa volatile, kompiler dapat mengoptimalkan akses memori dan menyimpan nilainya dalam register, jadi hanya penggunaan pertama variabel yang membaca lokasi memori aktual yang menyimpan variabel. Ini merupakan masalah jika variabel dimodifikasi oleh utas lain antara akses pertama dan kedua; utas pertama hanya memiliki salinan nilai pertama (pra-modifikasi), sehingga pernyataan if kedua menguji salinan basi dari nilai variabel.

0
David R Tribble