1. Apa itu Singleton Mode
Pola Singleton mengacu pada keberadaan hanya satu contoh sepanjang seluruh kehidupan aplikasi. Pola singleton adalah pola desain yang banyak digunakan. Ini memiliki banyak manfaat, yang dapat menghindari penciptaan duplikat objek instan, mengurangi overhead sistem pembuatan instance, dan menyimpan memori.
Ada tiga persyaratan untuk mode singleton:
2. Perbedaan antara pola singleton dan kelas statis
Pertama, mari kita mengerti apa kelas statis. Kelas statis berarti bahwa kelas memiliki metode statis dan bidang statis. Konstruktor dimodifikasi oleh pribadi, sehingga tidak dapat dipakai. Kelas matematika adalah kelas statis.
Setelah mengetahui apa kelas statis, mari kita bicara tentang perbedaan di antara mereka:
1) Pertama -tama, pola singleton akan memberi Anda objek yang unik secara global. Kelas statis hanya memberi Anda banyak metode statis. Metode -metode ini tidak perlu dibuat, dan dapat dipanggil langsung melalui kelas;
2) Pola singleton memiliki fleksibilitas yang lebih tinggi, dan metode dapat ditimpa, karena kelas statis semuanya merupakan metode statis, sehingga mereka tidak dapat ditimpa;
3) Jika itu adalah objek yang sangat berat, pola singleton bisa malas memuat, tetapi kelas statis tidak dapat melakukannya;
Kemudian, kelas statis harus digunakan, dan kapan kita harus menggunakan mode singleton? Pertama -tama, jika Anda hanya ingin menggunakan beberapa metode alat, yang terbaik adalah menggunakan kelas statis. Analogi statis lebih cepat daripada kelas singleton, karena pengikatan statis dilakukan selama periode kompilasi. Jika Anda ingin mempertahankan informasi status atau sumber daya akses, Anda harus menggunakan mode singleton. Dapat juga dikatakan bahwa ketika Anda membutuhkan kemampuan berorientasi objek (seperti warisan, polimorfisme), pilih kelas singleton, dan ketika Anda hanya menyediakan beberapa metode, pilih kelas statis.
3. Cara Menerapkan Mode Singleton
1. Mode pria lapar
Mode lapar yang disebut adalah memuat segera. Secara umum, contoh telah dihasilkan sebelum memanggil metode GetInstancef, yang berarti telah dihasilkan ketika kelas dimuat. Kerugian dari model ini sangat jelas, yaitu menempati sumber daya. Ketika kelas Singleton besar, kami sebenarnya ingin menggunakannya dan kemudian menghasilkan contoh. Oleh karena itu, metode ini cocok untuk kelas yang menempati lebih sedikit sumber daya dan akan digunakan selama inisialisasi.
kelas singletonhungary {private static singletonhungary singletonhungary = new singletonhungary (); // Atur konstruktor ke pribadi untuk melarang instantiasi melalui singletonhungary pribadi baru () {} public static singletonhungary getInstance () {return singletonhungary; }}2. Mode malas
Mode malas adalah pemuatan malas, juga disebut Lazy Loading. Buat contoh ketika program perlu digunakan, sehingga memori tidak akan terbuang. Untuk mode malas, berikut adalah 5 metode implementasi. Beberapa metode implementasi adalah utas-tidak aman, yang berarti bahwa masalah sinkronisasi sumber daya dapat terjadi dalam lingkungan konkurensi multi-utas.
Pertama-tama, metode pertama adalah bahwa tidak ada masalah di utas tunggal, tetapi akan ada masalah dalam multi-threading.
// Implementasi malas Singleton Mode 1-Thread Kelas Tidak Aman SingletonLazy1 {private static SingletonLazy SingletonLazy; private singletonLazy1 () {} public static singletonLazy1 getInstance () {if (null == singletonLazy) {coba {// simulasikan beberapa persiapan sebelum membuat objek thread.sleep (1000); } catch (InterruptedException e) {E.PrintStackTrace (); } singletonLazy = new singletonLazy1 (); } return singletonLazy; }}Mari kita simulasikan 10 utas asinkron untuk diuji:
kelas publik singletonLazyTest {public static void main (string [] args) {thread2 [] threadarr = new thread2 [10]; untuk (int i = 0; i <threadarr.length; i ++) {threadarr [i] = new thread2 (); Threadarr [i] .start (); }}} // Test Thread Class Thread2 Extends Thread {@Override public void run () {System.out.println (singletonLazy1.getInstance (). HashCode ()); }}Hasil Menjalankan:
124191239
124191239
872096466
1603289047
1698032342
1913667618
371739364
124191239
1723650563
367137303
Anda dapat melihat bahwa kode hash mereka tidak semuanya sama, yang berarti bahwa banyak objek dihasilkan dalam lingkungan multi-threaded, yang tidak memenuhi persyaratan pola singleton.
Jadi bagaimana cara membuat utas aman? Dalam metode kedua, kami menggunakan kata kunci yang disinkronkan untuk menyinkronkan metode GetInstance.
// Singleton Mode Lazy Implementasi 2-Safety Safety // Dengan mengatur metode sinkronisasi, efisiensinya terlalu rendah, seluruh metode terkunci kelas singletonLazy2 {private static singletonLazy2 singletonLazy; private singletonLazy2 () {} public static Synchronized singletonLazy2 getInstance () {try {if (null == singletonLazy) {// simulasi untuk melakukan beberapa persiapan sebelum membuat objek thread.sleep (1000); singletonLazy = singletonLazy baru (); }} catch (InterruptedException e) {E.PrintStackTrace (); } return singletonLazy; }}Menggunakan kelas uji di atas, hasil tes:
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
Seperti yang dapat dilihat, metode ini mencapai keamanan utas. Namun, kerugiannya adalah efisiensinya terlalu rendah dan berjalan secara serempak. Jika utas berikutnya ingin mendapatkan objek, itu harus menunggu utas sebelumnya untuk dirilis sebelum dapat terus mengeksekusi.
Maka kita tidak dapat mengunci metode ini, tetapi mengunci kode di dalamnya, yang juga dapat mencapai keamanan utas. Tetapi metode ini sama dengan metode sinkronisasi, dan juga berjalan secara sinkron dan memiliki efisiensi yang sangat rendah.
// implementasi singletonlazy 3-keselamatan thread // Dengan menetapkan blok kode sinkron, efisiensinya terlalu rendah, dan seluruh blok kode terkunci kelas singletonLazy3 {private static singletonLazy singletonlazy; singletonLazy private () {} public static singletonLazy3 getInstance () {coba {disinkronkan (singletonLazy3.class) {if (null == singletonLazy) {// simulate untuk melakukan beberapa persiapan sebelum membuat objek thread.sleep (1000); singletonLazy = singletonLazy baru (); }}} catch (InterruptedException e) {// todo: handle exception} return singletonLazy; }}Mari kita terus mengoptimalkan kode. Kami hanya mengunci kode yang membuat objek, tetapi dapatkah ini memastikan keamanan utas?
// Malas Implementasi Singleton Mode 4-Thread Uncafe // Hanya kode yang membuat instance disinkronkan dengan menetapkan blok kode sinkronisasi // tetapi masih ada masalah keamanan utas kelas singletonLazy4 {private static singletonLazy4 singletonLazy; Private singletonLazy4 () {} public static singletonLazy4 getInstance () {coba {if (null == singletonLazy) {// kode 1 // simulasi untuk melakukan beberapa persiapan sebelum membuat objek thread.sleep (1000); disinkronkan (singletonLazy4.class) {singletonLazy = singletonLazy baru (); // Kode 2}}} catch (InterruptedException e) {// TODO: Handle Exception} return singletonLazy; }}Mari kita lihat hasil berjalan:
1210004989
1425839054
1723650563
389001266
1356914048
389001266
1560241484
278778395
124191239
367137303
Menilai dari hasil, metode ini tidak dapat menjamin keamanan utas. Mengapa? Mari kita asumsikan bahwa dua utas A dan B pergi ke 'Kode 1' pada saat yang sama, karena objek masih kosong saat ini, sehingga keduanya dapat memasukkan metode ini. Putar pertama meraih kunci dan membuat objek. Setelah Thread B mendapatkan kunci, itu juga akan masuk ke 'Kode 2' saat dirilis, dan sebuah objek dibuat. Oleh karena itu, singleton tidak dapat dijamin dalam lingkungan multi-threaded.
Mari kita terus mengoptimalkan. Karena ada masalah dengan metode di atas, kita bisa membuat penilaian nol di blok kode sinkronisasi. Metode ini adalah mekanisme kunci periksa ganda DCL kami.
// Slazy Man dalam mode singleton mengimplementasikan 5-thread safety // dengan menetapkan blok kode sinkronisasi, gunakan DCL Double Check Lock Mechanism // Menggunakan Mekanisme Pengunci Periksa Double berhasil memecahkan masalah rasa tidak aman dan efisiensi yang diterapkan oleh PROGRIONAL//DCL juga merupakan solusi yang digunakan oleh sebagian besar multithon yang digabungkan dengan singleton. Ketika objek singletonLazy5 dibuat, ketika objek singletonLazy5 diperoleh, tidak perlu memverifikasi kunci blok kode sinkronisasi dan kode selanjutnya, dan secara langsung mengembalikan objek singletonLazy5 // fungsi penilaian detik: untuk menyelesaikan masalah keamanan di bawah multithreading, itu, untuk memastikan uniknya. kelas singletonLazy5 {private static volatile singletonLazy5 singletonLazy; private singletonLazy5 () {} public static singletonLazy5 getInstance () {coba {if (null == singletonLazy) {// simulasikan beberapa persiapan sebelum membuat objek thread.sleep (1000); disinkronkan (singletonLazy5.class) {if (null == singletonLazy) {singletonLazy = singletonLazy baru (); }}}} catch (InterruptedException e) {} return singletonLazy; }}Hasil Menjalankan:
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
Kita dapat melihat bahwa DCL Double Check Lock Mechanism memecahkan efisiensi dan masalah keselamatan utas dari mode malas memuat mode singleton. Ini juga metode yang paling sering kami gunakan.
kata kunci yang mudah menguap
Di sini saya perhatikan bahwa ketika mendefinisikan singletonLazy, kata kunci yang mudah menguap digunakan. Ini untuk mencegah instruksi untuk memesan ulang. Mengapa kita perlu melakukan ini? Mari kita lihat skenario:
Kode ini pergi ke singletonLazy = singletonLazy baru (); Tampaknya menjadi kalimat, tetapi ini bukan operasi atom (baik semua dieksekusi, atau semua tidak dieksekusi, dan setengahnya tidak dapat dieksekusi). Kalimat ini dikompilasi menjadi 8 instruksi perakitan, dan kira -kira 3 hal dilakukan:
1. Alokasikan memori ke instance SingletOnLazy5.
2. Inisialisasi konstruktor SingletOnLazy5
3. Tunjuk objek SingletOnLazy ke ruang memori yang dialokasikan (perhatikan bahwa instance ini tidak nol).
Karena kompiler Java memungkinkan prosesor untuk mengeksekusi out-of-order (out-order), dan urutan cache, register ke ingatan utama penulisan kembali di JMM (Java Memory Medel) sebelum JDK1.5, urutan poin kedua dan ketiga di atas tidak dapat dijamin. Dengan kata lain, perintah eksekusi mungkin 1-2-3 atau 1-3-2. Jika yang terakhir, dan sebelum 3 dieksekusi dan 2 tidak dieksekusi, ia dialihkan ke Thread 2. Pada saat ini, SingletonLazy telah mengeksekusi poin ketiga dalam utas satu, singletonLazy sudah tidak kosong, jadi utas dua secara langsung mengambil singletonLazy, kemudian menggunakannya, dan kemudian secara alami melaporkan kesalahan. Selain itu, kesalahan semacam ini yang sulit dilacak dan sulit direproduksi mungkin tidak ditemukan dalam minggu terakhir debugging.
Metode penulisan DCL untuk mengimplementasikan singleton direkomendasikan dalam banyak buku teknis dan buku teks (termasuk buku berdasarkan versi sebelumnya dari JDK1.4), tetapi sebenarnya tidak sepenuhnya benar. Memang, DCL layak dalam beberapa bahasa (seperti C), tergantung pada apakah urutan 2 dan 3 langkah dapat dijamin. Setelah JDK1.5, pejabat tersebut telah memperhatikan masalah ini, jadi JMM telah disesuaikan dan kata kunci yang mudah menguap telah dibatasi. Oleh karena itu, jika JDK adalah versi 1,5 atau lebih baru, Anda hanya perlu menambahkan kata kunci yang mudah menguap ke definisi SingletOnLazy, yang dapat memastikan bahwa SingletOnLazy dibaca dari memori utama setiap saat, dan pemesanan ulang dapat dilarang, dan Anda dapat menggunakan metode penulisan DCL untuk menyelesaikan mode singleton. Tentu saja, volatile akan lebih atau kurang mempengaruhi kinerja. Yang paling penting adalah bahwa kita juga perlu mempertimbangkan JDK1.42 dan versi sebelumnya, sehingga peningkatan penulisan pola singleton masih berlanjut.
3. Kelas Dalam Statis
Berdasarkan pertimbangan di atas, kita dapat menggunakan kelas dalam statis untuk mengimplementasikan pola singleton, kodenya adalah sebagai berikut:
// Menerapkan mode singleton dengan kelas internal statis-thread kelas keselamatan singletonstaticInner {private singletonstaticInner () {} private static class singletoninner {private static singletonstaticInner singletonstaticInner = singletonstaticinner () baru; } public static singletonstaticInner getInstance () {try {thread.sleep (1000); } catch (InterruptedException E) {// TODO Auto-Encanerated Catch Block E.PrintStackTrace (); } return singletoninner.singletonstaticInner; }}Dapat dilihat bahwa kita tidak secara eksplisit melakukan operasi sinkronisasi dengan cara ini, jadi bagaimana hal itu memastikan keamanan utas? Seperti mode Hungry Man, ini adalah fitur yang JVM memastikan bahwa anggota statis kelas hanya dapat dimuat sekali, sehingga hanya ada satu objek instance dari level JVM. Jadi pertanyaannya adalah, apa perbedaan antara metode ini dan model pria lapar? Bukankah itu segera memuat? Bahkan, ketika kelas dimuat, kelas dalamnya tidak akan dimuat pada saat yang sama. Kelas dimuat, yang terjadi ketika dan hanya jika salah satu anggota statisnya (domain statis, konstruktor, metode statis, dll.) Dipanggil.
Dapat dikatakan bahwa metode ini adalah solusi optimal untuk mengimplementasikan pola singleton.
4. Blok kode statis
Berikut adalah pola singleton implementasi blok statis. Metode ini mirip dengan yang pertama, dan juga model pria yang lapar.
// Gunakan blok kode statis untuk mengimplementasikan kelas mode singleton singletonstaticblock {private static singletonstaticblock singletonstaticblock; static {singletonstaticblock = singletonstaticblock () baru; } public static singletonstaticblock getInstance () {return singletonstaticblock; }}5. Serialisasi dan deserialisasi
Mengapa LZ merekomendasikan serialisasi dan deserialisasi? Karena meskipun mode singleton dapat memastikan keamanan utas, banyak objek akan dihasilkan dalam kasus serialisasi dan deserialisasi. Jalankan kelas tes berikut,
kelas publik singletonsticInnersererializetest {public static void main (string [] args) {coba {singletonstaticInnerSerialize serialize = singletonstaticinnerserialize.getInstance (); System.out.println (serialize.hashcode ()); // serialize fileOutputStream fo = new FileOutputStream ("tem"); ObjectOutputStream oo = ObjectOutputStream baru (FO); oo.writeObject (serialize); oo.close (); fo.close (); // Deserialize FileInputStream fi = FileInputStream baru ("tem"); ObjectInputStream oi = ObjectInputStream baru (FI); Singletonstaticinnerserialize serialize2 = (singletonstaticinnerserialize) oi.readObject (); oi.close (); fi.close (); System.out.println (serialize2.hashcode ()); } catch (Exception e) {E.PrintStackTrace (); }}} // Gunakan kelas internal anonim untuk mengimplementasikan pola singleton. Saat menemukan serialisasi dan deserialisasi, contoh yang sama tidak diperoleh.//solve masalah ini adalah dengan menggunakan metode ReadResolve selama serialisasi, yaitu, hapus bagian dari komentar. Kelas singletonstaticinnerserialize mengimplementasikan serializable { / *** 28 Maret 2018* / private static final long serialversionuid = 1l; kelas statis private innerclass {private static singletonstaticInnerserialize singletonstaticInnerSerialize = singletonstaticinnerserialize () baru; } public static singletonstaticInnerSerialize getInstance () {return innerclass.singletonstaticinnernerSerialize; } // Objek yang Dilindungi ReadResolve () {// System.out.println ("Metode ReadResolve dipanggil"); // return innerclass.singletonsticinnersererialize; //}}}Anda dapat melihat:
865113938
1078694789
Hasilnya menunjukkan bahwa memang dua contoh objek yang berbeda yang melanggar pola singleton. Jadi bagaimana cara menyelesaikan masalah ini? Solusinya adalah dengan menggunakan metode readResolve () dalam deserialisasi, menghapus kode komentar di atas, dan menjalankannya lagi:
865113938
Metode ReadResolve dipanggil
865113938
Pertanyaannya adalah, siapa cara sakral dari metode readResolve ()? Bahkan, ketika JVM menghapus dan "merakit" objek baru dari memori, itu akan secara otomatis menyebut metode ReadResolve ini untuk mengembalikan objek yang kami tentukan, dan aturan singleton dijamin. Munculnya readResolve () memungkinkan pemrogram untuk mengontrol objek yang diperoleh melalui deserialisasi sendiri.
Di atas adalah semua konten artikel ini. Saya berharap ini akan membantu untuk pembelajaran semua orang dan saya harap semua orang akan lebih mendukung wulin.com.