แนวคิด:
รูปแบบ Singleton ใน Java เป็นรูปแบบการออกแบบทั่วไป รูปแบบ Singleton แบ่งออกเป็นสามประเภท: Lazy Singleton, Hungry Singleton และ Singleton ที่ลงทะเบียน
โหมด Singleton มีลักษณะดังต่อไปนี้:
1. มีเพียงอินสแตนซ์เดียวในคลาสซิงเกิลตัน
2. คลาส Singleton ต้องสร้างอินสแตนซ์ที่เป็นเอกลักษณ์ของตัวเอง
3. คลาส Singleton ต้องจัดเตรียมอินสแตนซ์นี้ให้กับวัตถุอื่น ๆ ทั้งหมด
รูปแบบ Singleton ช่วยให้มั่นใจได้ว่าคลาสมีเพียงหนึ่งอินสแตนซ์และสร้างมันเองและให้อินสแตนซ์นี้กับระบบทั้งหมด ในระบบคอมพิวเตอร์วัตถุไดรเวอร์สำหรับพูลเธรดแคชวัตถุบันทึกกล่องโต้ตอบเครื่องพิมพ์และการ์ดกราฟิกมักถูกออกแบบมาเป็นซิงเกิล แอปพลิเคชันเหล่านี้มีฟังก์ชั่นการทำงานของตัวจัดการทรัพยากรมากหรือน้อย คอมพิวเตอร์แต่ละเครื่องสามารถมีเครื่องพิมพ์หลายเครื่อง แต่เครื่องพิมพ์เครื่องพิมพ์เพียงเครื่องเดียวเท่านั้นที่สามารถใช้งานได้เพื่อหลีกเลี่ยงงานพิมพ์สองงานที่ถูกส่งออกไปยังเครื่องพิมพ์ในเวลาเดียวกัน คอมพิวเตอร์แต่ละเครื่องสามารถมีพอร์ตการสื่อสารได้หลายพอร์ตและระบบควรจัดการพอร์ตการสื่อสารเหล่านี้จากส่วนกลางเพื่อหลีกเลี่ยงพอร์ตการสื่อสารหนึ่งที่ถูกเรียกพร้อมกันโดยสองคำขอ ในระยะสั้นการเลือกแบบจำลองซิงเกิลคือการหลีกเลี่ยงรัฐที่ไม่สอดคล้องกันและหลีกเลี่ยงความเป็นทางการเมือง
นี่คือการแนะนำสองประเภท: ขี้เกียจและหิว
1. โหลดทันที/สไตล์หิว
ก่อนที่จะเรียกวิธีการอินสแตนซ์ได้ถูกสร้างรหัส:
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep1; คลาสสาธารณะ myobject {// กำลังโหลดตอนนี้ == Evil Mode ส่วนตัวคงที่ myObject myObject = ใหม่ myObject (); ส่วนตัว myObject () {} สาธารณะ myObject getInstance () {// เวอร์ชันรหัสนี้กำลังโหลดอยู่ตอนนี้ // ข้อเสียของรหัสเวอร์ชันนี้คือไม่สามารถมีตัวแปรอินสแตนซ์อื่น ๆ // เนื่องจากวิธี getInstance () ไม่ได้ซิงโครไนซ์ // - สร้างคลาสเธรด
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep1; คลาสสาธารณะ Mythread ขยายเธรด {@Override โมฆะสาธารณะเรียกใช้ () {system.out.println (myobject.getInstance (). hashcode ()); - สร้างคลาสรัน
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep1; การเรียกใช้ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {Mythread t1 = ใหม่ mythread (); MYTHREAD T2 = ใหม่ MYTHREAD (); MYTHREAD T3 = ใหม่ MYTHREAD (); t1.start (); t2.start (); t3.start (); - การรันผลลัพธ์
167772895
167772895
167772895
HashCode เป็นค่าเดียวกันซึ่งหมายความว่าวัตถุนั้นเหมือนกันซึ่งหมายความว่ามีการใช้โหมดการโหลดทันที
2. ขี้เกียจโหลด/ขี้เกียจ
อินสแตนซ์จะถูกสร้างขึ้นหลังจากวิธีการที่เรียกว่า แผนการดำเนินการสามารถนำการสร้างอินสแตนซ์ลงในตัวสร้างพารามิเตอร์ที่ไม่มีพารามิเตอร์เพื่อให้อินสแตนซ์ของวัตถุจะถูกสร้างขึ้นเมื่อมีการเรียกวิธีการเท่านั้น รหัส:
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep2; คลาสสาธารณะ MyObject {ส่วนตัว myObject myObject; ส่วนตัว myObject () {} สาธารณะ myObject getInstance () {// การโหลดล่าช้าถ้า (myObject! = null) {} else {myObject = new myObject (); } return myObject; - สร้างคลาสเธรด
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep2; คลาสสาธารณะ Mythread ขยายเธรด {@Override โมฆะสาธารณะเรียกใช้ () {system.out.println (myobject.getInstance (). hashcode ()); - สร้างคลาสรัน
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep2; การเรียกใช้ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {Mythread t1 = ใหม่ mythread (); t1.start (); -การรันผลลัพธ์
167772895
แม้ว่าอินสแตนซ์ของวัตถุจะถูกนำไปใช้ถ้ามันอยู่ในสภาพแวดล้อมแบบมัลติเธรด แต่หลายกรณีจะเกิดขึ้นซึ่งไม่ใช่รูปแบบซิงเกิล
เรียกใช้คลาสทดสอบ
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep2; การเรียกใช้ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {Mythread t1 = ใหม่ mythread (); MYTHREAD T2 = ใหม่ MYTHREAD (); MYTHREAD T3 = ใหม่ MYTHREAD (); MYTHREAD T4 = ใหม่ MYTHREAD (); MYTHREAD T5 = NEW MYTHREAD (); t1.start (); t2.start (); t3.start (); t4.start (); t5.start (); -การรันผลลัพธ์
980258163
1224717057
1851889404
243120504
1672864109
เนื่องจากมีปัญหาเราจึงต้องแก้ปัญหา โซลูชันแบบมัลติเธรดในโหมดขี้เกียจรหัส:
ทางออกแรก โดยทั่วไปส่วนใหญ่เพิ่มการซิงโครไนซ์และซิงโครไนซ์สามารถเพิ่มไปยังตำแหน่งที่แตกต่างกัน
วิธีแรกล็อค
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep3; คลาสสาธารณะ MyObject {ส่วนตัว myObject myObject; private myObject () {} ซิงโครไนซ์สาธารณะคงที่ myObject getInstance () {// การโหลดล่าช้าลอง {ถ้า (myObject! = null) {} else {// จำลองการเตรียมการบางอย่างก่อนที่จะสร้าง thread.sleep (2000); myObject = new myObject (); }} catch (interruptedException e) {e.printStackTrace (); } return myObject; -รูปแบบการซิงโครไนซ์แบบซิงโครไนซ์นี้ส่งผลให้ไม่มีประสิทธิภาพและวิธีการทั้งหมดถูกล็อค
รูปแบบการใช้งานที่ซิงโครไนซ์ครั้งที่สอง
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep3; คลาสสาธารณะ MyObject {ส่วนตัว myObject myObject; private myObject () {} public Static myObject getInstance () {// การโหลดล่าช้าลอง {ซิงโครไนซ์ (myObject.class) {ถ้า (myObject! = null) {} else {// จำลองการเตรียมงานบางอย่างก่อนที่จะสร้าง thread.sleep (2000); myObject = new myObject (); }}} catch (interruptedException e) {e.printStackTrace (); } return myObject; - วิธีนี้ยังมีประสิทธิภาพต่ำมาก รหัสทั้งหมดในวิธีการถูกล็อค คุณจะต้องล็อครหัสคีย์เท่านั้น แผนการใช้งานที่ประสานกันครั้งที่สาม
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep3;
คลาสสาธารณะ myObject {ส่วนตัว myObject myObject; private myObject () {} public Static myObject getInstance () {// การโหลดล่าช้าลอง {ถ้า (myObject! = null) {} else {// จำลองการเตรียมการบางอย่างก่อนที่จะสร้าง thread.sleep (2000); ซิงโครไนซ์ (myobject.class) {myObject = new myObject (); }}} catch (interruptedException e) {e.printStackTrace (); } return myObject; -นี่ดูเหมือนจะเป็นทางออกที่ดีที่สุด แต่หลังจากเรียกใช้แล้วฉันพบว่าจริง ๆ แล้วมันไม่ปลอดภัยที่ไม่ปลอดภัย
ผลลัพธ์:
1224717057
971173439
1851889404
1224717057
1672864109
ทำไม
แม้ว่าคำสั่งที่สร้างวัตถุจะถูกล็อค แต่มีเพียงหนึ่งเธรดเดียวเท่านั้นที่สามารถสร้างการสร้างได้ในแต่ละครั้งหลังจากเธรดแรกเข้ามาเพื่อสร้างวัตถุวัตถุเธรดที่สองยังคงสามารถสร้างได้ต่อไปเพราะเราล็อคคำสั่งการสร้างเท่านั้น
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep3; คลาสสาธารณะ MyObject {ส่วนตัว myObject myObject; private myObject () {} public Static myObject getInstance () {// การโหลดล่าช้าลอง {ถ้า (myObject! = null) {} else {// จำลองการเตรียมการบางอย่างก่อนที่จะสร้าง thread.sleep (2000); ซิงโครไนซ์ (myObject.class) {ถ้า (myObject == null) {myObject = new myObject (); }}}} catch (interruptedException e) {e.printStackTrace (); } return myObject; -เพียงเพิ่มการตัดสินอีกครั้งในล็อคเพื่อให้แน่ใจว่ามีซิงเกิลตัน นี่คือกลไกการตรวจสอบ DCL สองครั้ง
ผลลัพธ์มีดังนี้:
1224717057
1224717057
1224717057
1224717057
1224717057
3. ใช้คลาสคงที่ในตัวเพื่อใช้เคสเดียว
รหัสหลัก
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep4; คลาสสาธารณะ myobject {// วิธีชั้นเรียนชั้นเรียนคลาสส่วนตัวคลาสคงที่ myobjecthandler {ส่วนตัว myobject myObject = new myObject (); } สาธารณะ myObject () {} สาธารณะ myObject getInstance () {return myobjecthandler.myobject; - รหัสคลาสเธรด
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep4; คลาสสาธารณะ Mythread ขยายเธรด {@Override โมฆะสาธารณะเรียกใช้ () {system.out.println (myobject.getInstance (). hashcode ()); - วิ่งชั้นเรียน
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep4; การเรียกใช้ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {Mythread t1 = ใหม่ mythread (); MYTHREAD T2 = ใหม่ MYTHREAD (); MYTHREAD T3 = ใหม่ MYTHREAD (); MYTHREAD T4 = ใหม่ MYTHREAD (); MYTHREAD T5 = NEW MYTHREAD (); t1.start (); t2.start (); t3.start (); t4.start (); t5.start (); -ผลลัพธ์
1851889404
1851889404
1851889404
1851889404
1851889404
ผ่านคลาสคงที่ภายในจะได้รับรูปแบบ Singleton ที่ปลอดภัยจากด้าย
iv. ทำให้เป็นลำดับและ deserialize รูปแบบซิงเกิล
คลาสคงที่ในตัวสามารถบรรลุปัญหาด้านความปลอดภัยของเธรด แต่ถ้าคุณพบวัตถุที่เป็นอนุกรมผลลัพธ์ที่ได้จากวิธีการเริ่มต้นยังคงเป็นหลายกรณี
รหัส myObject
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep5; นำเข้า java.io.serializable; MyObject ระดับสาธารณะใช้งาน Serializable { / ** * * / ส่วนตัวคงที่สุดท้าย // เมธอดชั้นในคลาสส่วนตัวคลาสคงที่ myobjecthandler {ส่วนตัว myobject myObject myObject = new myObject (); } สาธารณะ myObject () {} สาธารณะ myObject getInstance () {return myobjecthandler.myobject; } // protected myObject readResOlve () {// system.out.println ("วิธีการ readResolve ถูกเรียกว่า!"); // return myobjecthandler.myobject; //}}} ธุรกิจ
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep5; นำเข้า java.io.file; นำเข้า java.io.fileinputstream; นำเข้า java.io.filenotfoundexception; นำเข้า Java.io.fileOutputstream; java.io.ObjectOutputStream; SaveAndread ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {ลอง {myObject myObject = myObject.getInstance (); fileOutputStream fosref = ใหม่ fileOutputStream (ไฟล์ใหม่ ("myObjectFile.txt")); ObjectOutputStream oosref = ใหม่ ObjectOutputStream (fosref); oosref.writeObject (myObject); oosref.close (); fosref.close (); System.out.println (myobject.hashCode ()); } catch (filenotfoundException e) {e.printStackTrace (); } catch (ioexception e) {e.printstacktrace (); } fileInputStream fisref; ลอง {fisref = ใหม่ fileInputStream (ไฟล์ใหม่ ("myobjectfile.txt")); ObjectInputStream iOSREF = ใหม่ ObjectInputStream (FISREF); myObject myObject = (myObject) iosref.readObject (); iosref.close (); fisref.close (); System.out.println (myobject.hashCode ()); } catch (filenotfoundException e) {e.printStackTrace (); } catch (ioexception e) {e.printstacktrace (); } catch (classnotFoundException e) {e.printStackTrace (); -ผลลัพธ์
970928725
1099149023
hashcodes สองตัวที่แตกต่างกันพิสูจน์ว่าพวกเขาไม่ใช่วัตถุเดียวกัน วิธีแก้ปัญหาเพิ่มรหัสต่อไปนี้
ได้รับการปกป้อง myObject readResOlve () {system.out.println ("วิธี readResolve ถูกเรียกว่า!"); ส่งคืน myobjecthandler.myobject; -เรียกว่าในระหว่าง deserialization คุณสามารถรับวัตถุเดียวกันได้
System.out.println (myobject.readresolve (). hashcode ());
ผลลัพธ์
1255301379
วิธี readresolve ถูกเรียก!
1255301379
HashCode เดียวกันพิสูจน์ว่าได้รับวัตถุเดียวกัน
5. ใช้บล็อกรหัสแบบคงที่เพื่อใช้เคสเดียว
รหัสในบล็อกรหัสคงที่ถูกดำเนินการแล้วเมื่อใช้คลาสดังนั้นคุณสมบัติของรหัสสแตติกอย่างรวดเร็วสามารถใช้เพื่อใช้โหมดกำไรอย่างง่าย
คลาส MyObject
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep6; คลาสสาธารณะ MyObject {อินสแตนซ์ myObject แบบคงที่ส่วนตัว = null; ส่วนตัว myObject () {super (); } คงที่ {อินสแตนซ์ = new myObject (); } สาธารณะ myObject getInstance () {return instance; - ชั้นเรียนด้าย
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep6; คลาสสาธารณะ Mythread ขยายเธรด {@Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (int i = 0; i <5; i ++) {system.out.println (myobject.getInstance (). hashcode () - วิ่งชั้นเรียน
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep6; การเรียกใช้ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {Mythread t1 = ใหม่ mythread (); MYTHREAD T2 = ใหม่ MYTHREAD (); MYTHREAD T3 = ใหม่ MYTHREAD (); MYTHREAD T4 = ใหม่ MYTHREAD (); MYTHREAD T5 = NEW MYTHREAD (); t1.start (); t2.start (); t3.start (); t4.start (); t5.start (); -ผลการทำงาน:
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
1678885403
รูปแบบ Singleton ที่ปลอดภัยจากเธรดได้รับความสำเร็จผ่านคุณสมบัติของการเรียกใช้บล็อกรหัสแบบคงที่เพียงครั้งเดียวเท่านั้น
6. ใช้ประเภทข้อมูล enum enum เพื่อใช้โหมด Singleton
ลักษณะของ enum enum และบล็อกรหัสคงที่มีความคล้ายคลึงกัน เมื่อใช้ enums คอนสตรัคเตอร์จะถูกเรียกโดยอัตโนมัติและยังสามารถใช้ในการใช้โหมดซิงเกิล
คลาส MyObject
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep7; นำเข้า java.sql.connection; นำเข้า java.sql.drivermanager; นำเข้า java.sql.sqlexception; การเชื่อมต่อการเชื่อมต่อส่วนตัว ส่วนตัว myObject () {ลอง {system.out.println ("โครงสร้างของ myObject ถูกเรียกว่า"); string url = "jdbc: mysql: //172.16.221.19: 3306/wechat_1? useunicode = true & catreatencoding = UTF-8"; ชื่อสตริง = "รูท"; สตริงรหัสผ่าน = "111111"; สตริง drivername = "com.mysql.jdbc.driver"; class.forname (Drivername); การเชื่อมต่อ = driverManager.getConnection (URL, ชื่อ, รหัสผ่าน); } catch (classnotFoundException e) {e.printStackTrace (); } catch (sqlexception e) {e.printstacktrace (); }} การเชื่อมต่อสาธารณะ getConnection () {การเชื่อมต่อส่งคืน; - ชั้นเรียนด้าย
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep7; คลาสสาธารณะ Mythread ขยายเธรด {@Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (int i = 0; i <5; i ++) {system.out.println - วิ่งชั้นเรียน
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep7; การเรียกใช้ระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {Mythread t1 = ใหม่ mythread (); MYTHREAD T2 = ใหม่ MYTHREAD (); MYTHREAD T3 = ใหม่ MYTHREAD (); MYTHREAD T4 = ใหม่ MYTHREAD (); MYTHREAD T5 = NEW MYTHREAD (); t1.start (); t2.start (); t3.start (); t4.start (); t5.start (); -การรันผลลัพธ์
เรียกว่าโครงสร้าง myObject
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
56823666
วิธีการเขียนข้างต้นเปิดเผยคลาสการแจงนับซึ่งละเมิด "หลักการความรับผิดชอบเดี่ยว" คุณสามารถใช้คลาสเพื่อห่อหุ้มการแจงนับ
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep8; นำเข้า java.sql.connection; นำเข้า java.sql.drivermanager; นำเข้า java.sql.sqlexception; ชั้นเรียนสาธารณะ myobject การเชื่อมต่อการเชื่อมต่อส่วนตัว ส่วนตัว myenumsingleton () {ลอง {system.out.println ("โครงสร้างของ myObject เรียกว่า"); string url = "jdbc: mysql: //172.16.221.19: 3306/wechat_1? useunicode = true & catreatencoding = UTF-8"; ชื่อสตริง = "รูท"; สตริงรหัสผ่าน = "111111"; สตริง drivername = "com.mysql.jdbc.driver"; class.forname (Drivername); การเชื่อมต่อ = driverManager.getConnection (URL, ชื่อ, รหัสผ่าน); } catch (classnotFoundException e) {e.printStackTrace (); } catch (sqlexception e) {e.printstacktrace (); }} การเชื่อมต่อสาธารณะ getConnection () {การเชื่อมต่อส่งคืน; }} การเชื่อมต่อแบบคงที่สาธารณะ getConnection () {return myenumsingleton.connectionFactory.getConnection (); - เปลี่ยนรหัสเธรด
แพ็คเกจ com.weishiyao.learn.day8.singleton.ep8; คลาสสาธารณะ Mythread ขยายเธรด {@Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (int i = 0; i <5; i ++) {system.out.println (myobject.getConnection (). hashcode (); - เป็นผลให้โครงสร้าง myObject เรียกว่า
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
1948356121
ข้างต้นสรุปสถานการณ์และโซลูชันที่พบเมื่อรวมโหมดดอกเบี้ยเดี่ยวเข้ากับมัลติเธรดเพื่อให้สามารถตรวจสอบได้เมื่อใช้ในภายหลัง