รูปแบบ Singleton เป็นวิธีที่ง่ายที่สุดในการทำความเข้าใจและวิธีที่ง่ายที่สุดในการเขียนโค้ดที่เขียนด้วยมือในรูปแบบการออกแบบ แต่มีไม่มากที่เกี่ยวข้องกับความรู้ดังนั้นจึงมักจะใช้เป็นคำถามสัมภาษณ์ โดยทั่วไปแล้ว Singletons จะถูกเขียนขึ้นในห้าวิธี: ขี้เกียจหิวหิวการล็อคตรวจสอบสองครั้งชั้นเรียนภายในและการแจงนับ เพื่อบันทึกกระบวนการเรียนรู้วิธีการเขียนแบบซิงเกิลทั่วไปหลายวิธีได้รวบรวมไว้ที่นี่
สีบรอนซ์ 5: (ขี้เกียจโหลด แต่ด้ายไม่ปลอดภัย)
เมื่อถูกถามเกี่ยวกับการใช้รูปแบบ Singleton ปฏิกิริยาแรกของหลายคนคือการเขียนรหัสต่อไปนี้รวมถึงคำสอนเดียวกันในตำราเรียน
Singleton คลาสสาธารณะ {อินสแตนซ์แบบสแตติกส่วนตัว; Private Singleton () {} Public Static Singleton GetInstance () {ถ้า (อินสแตนซ์ == null) {อินสแตนซ์ = ใหม่ singleton (); } return อินสแตนซ์; -รหัสนี้ง่ายและตรงไปตรงมาและใช้โหมดการโหลดล่าช้า แต่เธรดไม่ปลอดภัย การเรียกเมธอด getInstance () ในสภาพแวดล้อมแบบมัลติเธรดอาจทำให้หลายเธรดเพื่อป้อนบล็อกรหัสโปรแกรมของคำสั่ง IF
สไตล์ขี้เกียจ: ซิงโครไนซ์ (ขี้เกียจ-โหลด, เธรดปลอดภัย แต่ไม่มีประสิทธิภาพ)
ในการแก้ปัญหาข้างต้นวิธีที่ง่ายที่สุดคือการตั้งค่าวิธีการ getInstance () ทั้งหมดให้ซิงโครไนซ์
Singleton คลาสสาธารณะ {อินสแตนซ์แบบสแตติกส่วนตัว; Private Singleton () {} สาธารณะคงที่แบบคงที่ singleton getInstance () {ถ้า (อินสแตนซ์ == null) {อินสแตนซ์ = ใหม่ซิงเกิลตัน (); } return อินสแตนซ์; -แม้ว่ามันจะเป็นเธรดที่ปลอดภัยและการโหลดล่าช้า แต่ก็ไม่ได้ประสิทธิภาพ เพราะเมื่อใดก็ตามที่มีเพียงหนึ่งเธรดที่เรียกวิธี getInstance () อย่างไรก็ตามการดำเนินการที่ซิงโครไนซ์จะต้องมีเฉพาะในการโทรครั้งแรกนั่นคือเมื่อวัตถุอินสแตนซ์ซิงเกิลถูกสร้างขึ้นเป็นครั้งแรก รูปแบบนี้ทำให้เกิดเธรดเพียงหนึ่งเดียวในการเข้าถึงวิธี GetInstance () ในแต่ละครั้งแม้หลังจากสร้างซิงเกิลตันซึ่งสามารถนำไปสู่ปัญหาประสิทธิภาพที่อาจเกิดขึ้น สิ่งนี้นำไปสู่การล็อคตรวจสอบอีกครั้ง
สไตล์หิว: ฟิลด์สุดท้ายคงที่ (ไม่ได้โหลด)
วิธีนี้ง่ายมากเนื่องจากอินสแตนซ์ของซิงเกิลตันถูกประกาศว่าเป็นแบบคงที่และเริ่มต้นเมื่อคลาสถูกโหลดลงในหน่วยความจำเป็นครั้งแรกดังนั้นการสร้างวัตถุอินสแตนซ์จึงปลอดภัย (รับประกันโดยการใช้งาน JVM)
Public Class Singleton {// เริ่มต้นอินสแตนซ์ Singleton สุดท้ายคงที่ส่วนตัว = New Singleton (); Private Singleton () {} Public Static Singleton GetInstance () {// Singleton พร้อมอินสแตนซ์ผลตอบแทนจากโรงงานคงที่; -มันไม่ใช่โหมดการโหลดที่ขี้เกียจอินสแตนซ์จะเริ่มต้นตั้งแต่ต้นหลังจากโหลดคลาสแม้ว่าไคลเอนต์จะไม่เรียกเมธอด getInstance () สิ่งนี้จะนำไปสู่ข้อ จำกัด การใช้งานบางอย่าง: ตัวอย่างเช่นการสร้างอินสแตนซ์แบบซิงเกิลขึ้นอยู่กับพารามิเตอร์หรือไฟล์การกำหนดค่า ก่อนที่ GetInstance () จะต้องมีการเรียกใช้วิธีการบางอย่างเพื่อตั้งค่าพารามิเตอร์เพื่อไม่ให้ใช้วิธีการเขียนซิงเกิลตันนี้ วิธีการที่คล้ายกัน ได้แก่ :
Singleton คลาสสาธารณะ {อินสแตนซ์ Singleton สุดท้ายคงที่ = ใหม่ Singleton (); // ซิงเกิลตันพร้อมภาคสนามสุดท้ายสาธารณะซิงเกิลตัน () {}} // <มีผลบังคับใช้ java> หน้า 14 บอกความแตกต่างระหว่างทั้งสองล็อคตรวจสอบสองครั้ง + ผันผวน (lazyload, thread-safe, แต่คลุมเครือ)
รูปแบบการล็อคที่ตรวจสอบแล้วสองครั้งเป็นวิธีการล็อคโดยใช้บล็อกแบบซิงโครนัส โปรแกรมเมอร์เรียกมันว่าล็อคตรวจสอบสองครั้งเนื่องจากจะมีสองอินสแตนซ์ตรวจสอบ == null เมื่ออยู่นอกบล็อกการซิงโครไนซ์และอีกครั้งภายในบล็อกการซิงโครไนซ์ ทำไมเราต้องตรวจสอบอีกครั้งในบล็อกการซิงโครไนซ์? เนื่องจากหลายเธรดอาจเข้าสู่หากอยู่นอกบล็อกการซิงโครไนซ์ด้วยกันหากไม่มีการตรวจสอบรองในบล็อกการซิงโครไนซ์วัตถุอินสแตนซ์หลายวัตถุจะถูกสร้างขึ้น
สาธารณะคงที่ Singleton GetSingleton () {ถ้า (อินสแตนซ์ == null) {// single checked synchronized (singleton.class) {ถ้า (อินสแตนซ์ == null) {// อินสแตนซ์ตรวจสอบสองครั้ง = ใหม่ singleton (); }}} return instance;}รหัสนี้ดูสมบูรณ์แบบ แต่น่าเสียดายที่มันเป็นปัญหา สิ่งสำคัญคืออินสแตนซ์ประโยค = ใหม่ซิงเกิล () นี่ไม่ใช่การผ่าตัดอะตอม ในความเป็นจริงประโยคนี้ใน JVM นั้นประมาณ 3 สิ่งต่อไปนี้
อย่างไรก็ตามมีการเพิ่มประสิทธิภาพการสั่งซื้อการสั่งซื้อใหม่ในคอมไพเลอร์ JIT ของ JVM กล่าวอีกนัยหนึ่งคำสั่งของขั้นตอนที่สองและสามข้างต้นไม่สามารถรับประกันได้และลำดับการดำเนินการขั้นสุดท้ายอาจเป็น 1-2-3 หรือ 1-3-2 หากเป็นหลังมันจะถูกจองโดยเธรด 2 ก่อนที่จะดำเนินการ 3 และ 2 จะไม่ถูกดำเนินการ ในเวลานี้อินสแตนซ์นั้นไม่ใช่ NULL (แต่ไม่ได้เริ่มต้น) ดังนั้นเธรด 2 จะส่งคืนอินสแตนซ์โดยตรงจากนั้นใช้แล้วรายงานข้อผิดพลาดตามธรรมชาติ ในการทำเช่นนี้เราต้องประกาศตัวแปรอินสแตนซ์ว่ามีความผันผวน
Singleton คลาสสาธารณะ {อินสแตนซ์ Singleton แบบคงที่ส่วนตัว // ประกาศว่าเป็น Singleton ส่วนตัวที่ผันผวน () {} Singleton Static Public Static GetSingleton () {ถ้า (อินสแตนซ์ == null) {ซิงโครไนซ์ (singleton.class) {ถ้า (อินสแตนซ์ == null) {อินสแตนซ์ = new singleton (); }} ส่งคืนอินสแตนซ์; -อย่างไรก็ตามมันเป็นสิ่งสำคัญที่จะต้องทราบว่ายังมีปัญหากับการล็อคการตรวจสอบสองครั้งในเวอร์ชันก่อน Java 1.5 ปัญหานี้ได้รับการแก้ไขใน Java 1.5 เท่านั้นดังนั้นคุณสามารถใช้ความผันผวนได้หลังจากนั้น
ชั้นเรียนภายในแบบคงที่: IODH, ผู้เริ่มต้นตามความต้องการ
รูปแบบนี้รวมคลาสภายในแบบคงที่ของ Java และความรู้การล็อคการซิงโครไนซ์เริ่มต้นแบบหลายเธรดและใช้ทั้งการโหลดเวลาแฝงและความปลอดภัยของเธรด
Public Class Singleton {Private Singleton () {} Lazy Holder ชั้นเรียนแบบคงที่ส่วนตัว {อินสแตนซ์ Singleton สุดท้ายคงที่ = ใหม่ Singleton (); } สาธารณะคงที่ Singleton GetInstance () {// จาก Wikipedia return lazyholder.instance; -ชั้นเรียนชั้นในแบบคงที่เทียบเท่ากับส่วนคงที่ของชั้นนอก วัตถุของมันไม่ได้ขึ้นอยู่กับวัตถุคลาสภายนอกดังนั้นจึงสามารถสร้างได้โดยตรง คลาสภายในแบบคงที่จะถูกทำซ้ำเมื่อใช้เป็นครั้งแรกเท่านั้น
ล็อคการซิงโครไนซ์เริ่มต้นแบบมัลติเธรด
ดังที่เราทุกคนรู้ในการพัฒนาแบบหลายเธรดเพื่อแก้ปัญหาพร้อมกันส่วนใหญ่จะใช้การซิงโครไนซ์เพื่อเพิ่ม mutexes สำหรับการควบคุมการซิงโครไนซ์ แต่ในบางกรณี JVM ได้ทำการซิงโครไนซ์โดยปริยายสำหรับคุณแล้วและในกรณีเหล่านี้ไม่จำเป็นต้องควบคุมการซิงโครไนซ์ด้วยตนเอง สถานการณ์เหล่านี้รวมถึง :
1. เมื่อเริ่มต้นข้อมูลด้วยการเริ่มต้นแบบคงที่ (initializer บนฟิลด์คงที่หรือในบล็อก {} คงที่)
2. เมื่อเข้าถึงฟิลด์สุดท้าย
3. เมื่อสร้างวัตถุก่อนสร้างเธรด
4. เมื่อเธรดสามารถเห็นวัตถุมันจะดำเนินการ
enum
เริ่มต้นจาก Java 1.5 เพียงแค่เขียนประเภท enum ที่มีองค์ประกอบเดียว:
Public Enum Singleton {อินสแตนซ์;}วิธีนี้มีหน้าที่คล้ายกับวิธีการโดเมนสาธารณะ แต่มันก็กระชับมากขึ้นและให้กลไกการทำให้เป็นอนุกรมโดยไม่ต้องเสียค่าใช้จ่ายป้องกันการสร้างอินสแตนซ์หลายครั้งอย่างแน่นอนแม้ว่าจะต้องเผชิญกับการทำให้เป็นอนุกรมที่ซับซ้อนหรือการโจมตีแบบสะท้อนกลับ แม้ว่าวิธีการนี้จะไม่ได้รับการยอมรับอย่างกว้างขวาง แต่ประเภทการแจงนับขององค์ประกอบเดียวได้กลายเป็นวิธีที่ดีที่สุดในการใช้ซิงเกิล
-
1. รายละเอียดของการสุดท้ายคืออะไร
2. การกำหนดค่าเริ่มต้นการกำหนดที่ฟิลด์คงที่ตามลำดับและบล็อกรหัสคงที่หรือไม่?
3. วิธีการเขียนรูปแบบซิงเกิลสำหรับชั้นเรียนภายในแบบคงที่
4. ตัวอย่างในการวิเคราะห์รูปแบบการออกแบบ Java EE และแอปพลิเคชันมีเอฟเฟกต์ Lazyload จริงหรือไม่?
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะช่วยในการศึกษาหรือทำงานของทุกคน ฉันหวังว่าจะสนับสนุน Wulin.com เพิ่มเติม!