Saya menemukan situasi selama debugging kode, yaitu ketika saya memeriksa koneksi memcached, saya menemukan bahwa itu selalu dipertahankan sekitar 100. Tentu saja, ini sepertinya tidak ada masalah, karena memcached memiliki 1024 koneksi secara default. Tapi yang saya pikirkan adalah mengapa ada 100 atau lebih. Karena memcachedclient saya dibuat dalam mode tunggal, saya mendefinisikan kelas memcachedClientFactory.
pribadi MemcachedClientFactory(){
}
private MemcachedClientFactory(MemcachedConnectionBuilder memcachedConnectionBuilder, server String){
this.memcachedConnectionBuilder= memcachedConnectionBuilder;
this.server=server;
}
MemcachedClient statis publik createClient(){
jika(memcachedClient==null){
this.memcahcedClien= MemcachedClient baru(memcachedConnectionBuilder.build(),AddrUtil.get(server));
}
kembalikan this.memcahcedClient;
}
}
}
Kembali ke pertanyaan awal, kenapa koneksinya lebih dari 100?
Apakah cara penulisan di atas benar-benar bisa menjamin hanya satu koneksi saja yang akan dihasilkan? Jelas tidak, kenapa? Konkurensi multi-utas! Masalahnya terletak di sini. Ketika beberapa thread memasuki metode createClient() secara bersamaan, dan semuanya memutuskan bahwa memcachedClient adalah null, banyak koneksi akan dihasilkan. Ha, masalahnya sudah ditemukan.
memperbaiki:
Tidak apa-apa, perubahannya sangat sederhana. Tidak ada masalah pada programnya, dan dijamin hanya ada satu koneksi.
Namun mengesampingkan masalah ini, kita dapat terus memikirkan secara mendalam tentang bagaimana menyelesaikan masalah konkurensi dalam mode singleton.
Izinkan saya meringkas, kira-kira ada tiga cara untuk menyelesaikan masalah konkurensi dalam mode tunggal:
1. Jangan gunakan instantiasi tertunda, tetapi gunakan instantiasi awal.
Artinya, program ini ditulis ulang sebagai:
getInstance Singleton statis publik(){
contoh kembali;
}
}
Saat melakukan ini, jvm segera membuat instance saat memuat kelas, jadi premisnya adalah bahwa beban pembuatan instance tidak besar, saya tidak terlalu memikirkan kinerja, dan kami mengonfirmasi bahwa instance tersebut pasti akan digunakan. Sebenarnya kode saya sebelumnya juga bisa digunakan dengan cara ini:
pribadi MemcachedClientFactory(){
}
private MemcachedClientFactory(MemcachedConnectionBuilder memcachedConnectionBuilder, server String){
this.memcachedConnectionBuilder= memcachedConnectionBuilder;
this.server=server;
}
MemcachedClient statis publik createClient(){
kembalikan this.memcahcedClient;
}
}
}
Namun sepertinya tidak ada masalah, namun ada bahaya yang tersembunyi, yaitu jika seseorang secara tidak sengaja memanggil metode memcachedClient.shutdown(), seluruh program tidak akan dapat menghasilkan memcachedClient baru. Tentu saja ini merupakan kasus ekstrem, namun demi ketahanan kode, ini dapat diubah menjadi:
2. Cukup gunakan kata kunci tersinkronisasi.
Melakukan hal ini dapat memastikan masalah sinkronisasi, tetapi kami tahu bahwa overhead penggunaan sinkronisasi sangat tinggi dan akan berdampak serius pada kinerja, jadi premis penggunaan ini adalah Anda mengonfirmasi bahwa Anda tidak akan sering memanggil metode ini, atau bahwa overhead pembuatan kejadian ini tidak akan menjadi sesuatu yang istimewa. Apakah dapat ditingkatkan, lihat di bawah.
3. Gunakan "periksa ulang penguncian" dan gunakan sinkronisasi di getInstance
pribadi Singleton(){};
getInstance Singleton statis publik(){
jika(contoh==batal){
disinkronkan (Singleton.class){
jika(contoh==batal){
contoh=New Singleton();
}
}
}
contoh kembali;
}
}