Penguncian Semak Berganda dengan Singleton

1. Pengenalan

Dalam tutorial ini, kita akan membincangkan mengenai corak reka bentuk penguncian yang diperiksa dua kali. Corak ini mengurangkan bilangan pemerolehan kunci dengan hanya memeriksa keadaan penguncian terlebih dahulu. Akibatnya, biasanya ada peningkatan prestasi.

Mari kita lihat lebih mendalam bagaimana ia berfungsi.

2. Pelaksanaan

Sebagai permulaan, mari kita pertimbangkan singleton sederhana dengan penyegerakan draconian:

public class DraconianSingleton { private static DraconianSingleton instance; public static synchronized DraconianSingleton getInstance() { if (instance == null) { instance = new DraconianSingleton(); } return instance; } // private constructor and other methods ... }

Walaupun kelas ini selamat digunakan, kami dapat melihat bahawa terdapat kelemahan prestasi yang jelas: setiap kali kami ingin mendapatkan contoh single kami, kami perlu memperoleh kunci yang mungkin tidak diperlukan.

Untuk memperbaikinya, kita boleh mulai dengan mengesahkan jika kita perlu membuat objek di tempat pertama dan hanya dalam kes itu kita akan memperoleh kunci.

Melangkah lebih jauh, kami ingin melakukan pemeriksaan yang sama sekali lagi setelah memasuki blok yang disegerakkan, untuk memastikan operasi tidak tetap:

public class DclSingleton { private static volatile DclSingleton instance; public static DclSingleton getInstance() { if (instance == null) { synchronized (DclSingleton .class) { if (instance == null) { instance = new DclSingleton(); } } } return instance; } // private constructor and other methods... }

Satu perkara yang perlu diingat dengan corak ini ialah bidang perlu berubah-ubah untuk mengelakkan masalah ketidakcocokan cache. Sebenarnya, model memori Java membenarkan penerbitan objek yang diinisialisasi sebahagian dan ini boleh menyebabkan bug halus.

3. Alternatif

Walaupun penguncian dua kali berpotensi mempercepat, ia mempunyai sekurang-kurangnya dua masalah:

  • kerana memerlukan kata kunci yang tidak stabil untuk berfungsi dengan baik, kata kunci itu tidak serasi dengan Java 1.4 dan versi yang lebih rendah
  • ia agak verbose dan menjadikan kod sukar dibaca

Atas sebab-sebab ini, mari kita melihat beberapa pilihan lain tanpa kekurangan ini. Semua kaedah berikut menyerahkan tugas penyegerakan ke JVM.

3.1. Permulaan Awal

Cara termudah untuk mencapai keselamatan benang adalah dengan menyusun penciptaan objek atau menggunakan blok statik yang setara. Ini memanfaatkan fakta bahawa medan dan blok statik diinisialisasi satu demi satu (Spesifikasi Bahasa Java 12.4.2):

public class EarlyInitSingleton { private static final EarlyInitSingleton INSTANCE = new EarlyInitSingleton(); public static EarlyInitSingleton getInstance() { return INSTANCE; } // private constructor and other methods... }

3.2. Permulaan berdasarkan Permintaan

Selain itu, kerana kita tahu dari rujukan Spesifikasi Bahasa Java pada perenggan sebelumnya bahawa inisialisasi kelas terjadi pada kali pertama kita menggunakan salah satu kaedah atau bidangnya, kita dapat menggunakan kelas statis bersarang untuk melaksanakan inisialisasi malas:

public class InitOnDemandSingleton { private static class InstanceHolder { private static final InitOnDemandSingleton INSTANCE = new InitOnDemandSingleton(); } public static InitOnDemandSingleton getInstance() { return InstanceHolder.INSTANCE; } // private constructor and other methods... }

Dalam kes ini, kelas InstanceHolder akan menetapkan bidang tersebut pada kali pertama kami mengaksesnya dengan meminta getInstance.

3.3. Enum Singleton

Penyelesaian terakhir datang dari buku Java Berkesan (Item 3) oleh Joshua Block dan menggunakan enum dan bukannya kelas . Pada masa penulisan, ini dianggap sebagai kaedah paling ringkas dan selamat untuk menulis lagu tunggal:

public enum EnumSingleton { INSTANCE; // other methods... }

4. Kesimpulan

Kesimpulannya, artikel ringkas ini melalui corak penguncian yang diperiksa dua kali, hadnya dan beberapa alternatif.

Dalam praktiknya, keterlaluan dan kekurangan keserasian yang mundur menjadikan corak ini rawan kesalahan dan oleh itu kita harus menjauhinya. Sebaliknya, kita harus menggunakan alternatif yang membolehkan JVM melakukan penyegerakan.

Seperti biasa, kod semua contoh boleh didapati di GitHub.