ฉันค้นพบสถานการณ์ระหว่างการแก้ไขโค้ด นั่นคือเมื่อฉันตรวจสอบการเชื่อมต่อของ memcached ฉันพบว่ามันถูกรักษาไว้ที่ประมาณ 100 เสมอ แน่นอนว่าดูเหมือนว่าจะไม่มีปัญหา เนื่องจาก memcached มีการเชื่อมต่อ 1,024 รายการตามค่าเริ่มต้น แต่สิ่งที่ฉันคิดคือเหตุใดจึงมี 100 หรือประมาณนั้น เนื่องจาก memcachedclient ของฉันถูกสร้างขึ้นในโหมดซิงเกิลตัน ฉันจึงกำหนดคลาส memcachedClientFactory รหัสหลักจึงเป็นดังนี้:
MemcachedClientFactory ส่วนตัว () {
-
MemcachedClientFactory ส่วนตัว (MemcachedConnectionBuilder memcachedConnectionBuilder, เซิร์ฟเวอร์สตริง) {
this.memcachedConnectionBuilder= memcachedConnectionBuilder;
นี้.เซิร์ฟเวอร์=เซิร์ฟเวอร์;
-
MemcachedClient แบบคงที่สาธารณะ createClient () {
ถ้า (memcachedClient==null){
this.memcahcedClien= MemcachedClient ใหม่ (memcachedConnectionBuilder.build(),AddrUtil.get(เซิร์ฟเวอร์));
-
ส่งคืน this.memcahcedClient;
-
-
-
กลับมาที่คำถามเดิมว่าเหตุใดจึงมีการเชื่อมต่อมากกว่า 100 รายการ?
วิธีการเขียนข้างต้นสามารถรับประกันได้ว่าจะสร้างการเชื่อมต่อเดียวเท่านั้นหรือไม่ แน่นอนว่าไม่ เพราะเหตุใด? พร้อมกันหลายเธรด! ปัญหาอยู่ที่นี่ เมื่อหลายเธรดเข้าสู่เมธอด createClient() พร้อมกัน และเธรดทั้งหมดตัดสินใจว่า memcachedClient เป็นโมฆะ การเชื่อมต่อหลายรายการจะถูกสร้างขึ้น ฮา พบปัญหาแล้ว
ทำให้ดีขึ้น:
ไม่เป็นไร การเปลี่ยนแปลงนั้นง่ายมาก ไม่มีปัญหากับโปรแกรมและรับประกันว่ามีการเชื่อมต่อเพียงจุดเดียว
แต่การแยกปัญหานี้ออกไป เราสามารถคิดอย่างลึกซึ้งต่อไปเกี่ยวกับวิธีแก้ปัญหาการทำงานพร้อมกันในโหมดซิงเกิลตันได้
โดยสรุป มีประมาณสามวิธีในการแก้ปัญหาการทำงานพร้อมกันในโหมดซิงเกิลตัน:
1. อย่าใช้การสร้างอินสแตนซ์แบบล่าช้า แต่ใช้การสร้างอินสแตนซ์ล่วงหน้า
นั่นคือโปรแกรมถูกเขียนใหม่เป็น:
สาธารณะ singleton getInstance () {
ส่งคืนอินสแตนซ์;
-
-
เมื่อทำสิ่งนี้ jvm จะสร้างอินสแตนซ์ทันทีเมื่อโหลดคลาส ดังนั้นสมมติฐานของสิ่งนี้ก็คือภาระในการสร้างอินสแตนซ์ไม่ใหญ่นัก ฉันไม่คิดมากเกี่ยวกับประสิทธิภาพการทำงานมากเกินไป และเรายืนยันว่าอินสแตนซ์จะแน่นอน ถูกนำมาใช้ อันที่จริง รหัสก่อนหน้าของฉันสามารถใช้ในลักษณะนี้ได้เช่นกัน:
MemcachedClientFactory ส่วนตัว () {
-
MemcachedClientFactory ส่วนตัว (MemcachedConnectionBuilder memcachedConnectionBuilder, เซิร์ฟเวอร์สตริง) {
this.memcachedConnectionBuilder= memcachedConnectionBuilder;
นี้.เซิร์ฟเวอร์=เซิร์ฟเวอร์;
-
MemcachedClient แบบคงที่สาธารณะ createClient () {
ส่งคืน this.memcahcedClient;
-
-
-
อย่างไรก็ตาม ดูเหมือนว่าจะไม่มีปัญหา แต่มีอันตรายซ่อนอยู่ นั่นคือ เมื่อมีคนเรียกใช้เมธอด memcachedClient.shutdown() โดยไม่ได้ตั้งใจ โปรแกรมทั้งหมดจะไม่สามารถสร้าง memcachedClient ใหม่ได้ แน่นอนว่านี่เป็นกรณีที่รุนแรง แต่เพื่อความคงทนของโค้ด สามารถเปลี่ยนเป็น:
2. เพียงใช้คำหลักที่ซิงโครไนซ์
การทำเช่นนี้สามารถรับประกันปัญหาการซิงโครไนซ์ แต่เรารู้ว่าค่าใช้จ่ายในการใช้งานซิงโครไนซ์นั้นสูงมาก และจะส่งผลต่อประสิทธิภาพการทำงานอย่างจริงจัง ดังนั้น หลักฐานของการใช้สิ่งนี้ก็คือคุณยืนยันว่าคุณจะไม่เรียกใช้วิธีนี้บ่อยครั้ง หรือค่าใช้จ่ายในการสร้าง กรณีนี้จะไม่พิเศษ สามารถปรับปรุงได้หรือไม่ดูด้านล่าง
3. ใช้ "double check locking" และใช้การซิงโครไนซ์ใน getInstance
ซิงเกิลตันส่วนตัว(){};
สาธารณะ singleton getInstance () {
ถ้า (อินสแตนซ์ == null) {
ซิงโครไนซ์ (Singleton.class) {
ถ้า (อินสแตนซ์ == null) {
อินสแตนซ์ = ใหม่ซิงเกิลตัน ();
-
-
-
ส่งคืนอินสแตนซ์;
-
-