مفهوم:
نمط Singleton في Java هو نمط تصميم شائع. ينقسم نمط Singleton إلى ثلاثة أنواع: Lazy Singleton ، Hungry Singleton ، و Singleton المسجل.
يحتوي وضع Singleton على الخصائص التالية:
1. يمكن أن يكون هناك مثيل واحد فقط في فصل المفرد.
2. يجب أن تنشئ فئة Singleton مثيلها الفريد.
3. يجب أن توفر فئة Singleton هذه المثيل لجميع الكائنات الأخرى.
يضمن نمط Singleton أن يكون لدى الفصل مثيلًا واحدًا فقط ، ويستند إلى مثيله ويوفر هذا الحالة للنظام بأكمله. في أنظمة الكمبيوتر ، غالبًا ما يتم تصميم كائنات برنامج التشغيل لتجمعات مؤشرات الترابط ، وذاكرة التخزين المؤقت ، وكائنات السجل ، ومربعات الحوار ، والطابعات ، وبطاقات الرسومات كفرد. هذه التطبيقات لها وظائف مدير الموارد إلى حد ما. يمكن أن يحتوي كل كمبيوتر على العديد من الطابعات ، ولكن يمكن أن يكون التخزين المؤقت للطابعة الواحدة متاحًا لتجنب وظيفتين للطباعة المخرجين إلى الطابعة في نفس الوقت. يمكن أن يكون لكل جهاز كمبيوتر العديد من منافذ الاتصالات ، ويجب أن يدير النظام مركزيًا منافذ الاتصال هذه لتجنب ميناء اتصال واحد يتم استدعاؤه في وقت واحد بواسطة طلبين. باختصار ، فإن اختيار نموذج Singleton هو تجنب الدول غير المتسقة وتجنب الصعوبة السياسية.
فيما يلي نوعان من المقدمات: كسول وجائع
1. تحميل على الفور/نمط الجائع
قبل استدعاء الطريقة ، تم إنشاء المثيل ، رمز:
package com.weishiyao.learn.day8.singleton.ep1 ؛ الطبقة العامة myobject {// loading now == evil mode private static myobject myobject = new myobject () ؛ MyObject الخاص () {} عام ثابت عام myobject getInstance () {// يتم تحميل إصدار الرمز هذا الآن // عيب هذا الإصدار من الكود هو أنه لا يمكن أن يكون هناك متغيرات مثيل أخرى // لأن طريقة getInstance () غير متزامنة // لذلك ، قد تحدث مشكلات غير موجودة ؛ }} قم بإنشاء فئة مؤشر ترابط
package com.weishiyao.learn.day8.singleton.ep1 ؛ الطبقة العامة myThread تمتد الموضوع {Override public void run () {system.out.println (myobject.getInstance (). hashcode ()) ؛ }} إنشاء فئة تشغيل
package com.weishiyao.learn.day8.singleton.ep1 ؛ الطبقة العامة تشغيل {public static void main (string [] args) {mythread t1 = new MyThread () ؛ mythread t2 = new MyThread () ؛ mythread t3 = new MyThread () ؛ t1.start () ؛ t2.start () ؛ t3.start () ؛ }} نتائج التشغيل
167772895
167772895
167772895
HashCode هو نفس القيمة ، مما يعني أن الكائن هو نفسه أيضًا ، مما يعني أنه يتم تنفيذ وضع التحميل الفوري.
2. الكسول التحميل/كسول
سيتم إنشاء المثيل بعد استدعاء الطريقة. يمكن أن تكون خطة التنفيذ هي وضع مثيل في المُنشئ بدون المعلمة ، بحيث يتم إنشاء مثيل للكائن فقط عندما يتم استدعاء الطريقة. شفرة:
حزمة com.weishiyao.learn.day8.singleton.ep2 ؛ الطبقة العامة myobject {private static myobject myobject ؛ private myobject () {} myobject getInstance () {// تأخير تحميل if (myObject! = null) {} آخر {myObject = new myobject () ؛ } إرجاع myobject ؛ }} قم بإنشاء فئة مؤشر ترابط
Package com.weishiyao.learn.day8.singleton.ep2 ؛ الفئة العامة myThread تمتد الموضوع {Override public void run () {system.out.println (myobject.getInstance (). hashcode ()) ؛ }} إنشاء فئة تشغيل
package com.weishiyao.learn.day8.singleton.ep2 ؛ الطبقة العامة تشغيل {public static void main (string [] args) {mythread t1 = new MyThread () ؛ t1.start () ؛ }}نتائج التشغيل
167772895
على الرغم من أن مثيلًا لكائن ما ، إذا كان في بيئة متعددة الخيوط ، ستحدث حالات متعددة ، وهو ليس نمطًا مفردة
قم بتشغيل فئة الاختبار
package com.weishiyao.learn.day8.singleton.ep2 ؛ الطبقة العامة تشغيل {public static void main (string [] args) {mythread t1 = new MyThread () ؛ mythread t2 = new MyThread () ؛ mythread t3 = new MyThread () ؛ mythread t4 = new MyThread () ؛ mythread t5 = new MyThread () ؛ t1.start () ؛ t2.start () ؛ t3.start () ؛ t4.start () ؛ t5.start () ؛ }}نتائج التشغيل
980258163
1224717057
1851889404
188820504
1672864109
نظرًا لوجود مشكلة ، نحتاج إلى حل المشكلة. الحل متعدد مؤشرات الترابط في الوضع البطيء ، الكود:
يمكن إضافة الحل الأول ، الأكثر شيوعًا ، المزامنة ، والمزامنة إلى مواقع مختلفة
الطريقة الأولى تغلق
حزمة com.weishiyao.learn.day8.singleton.ep3 ؛ الطبقة العامة myobject {private static myobject myobject ؛ MyObject الخاص () {} متزامن MyObject MyObject GetInstance () {// تأخير تحميل جرب {if (myobject! = null) {} آخر {// محاكاة بعض التحضير قبل إنشاء موضوع كائن (2000) ؛ myobject = new myobject () ؛ }} catch (interruptedException e) {E.PrintStackTrace () ؛ } إرجاع myobject ؛ }}ينتج عن مخطط التزامن المتزامن هذا غير فعال للغاية ويتم قفل الطريقة بأكملها
مخطط الاستخدام المتزامن الثاني
حزمة com.weishiyao.learn.day8.singleton.ep3 ؛ الطبقة العامة myobject {private static myobject myobject ؛ private myObject () {} myobject getInstance () {// تأخير تحميل حاول {synchronized (myobject.class) {if (myObject! = null) {} آخر {// محاكاة بعض أعمال التحضير قبل إنشاء موضوع كائن. sleep (2000) ؛ myobject = new myobject () ؛ }}} catch (interruptedException e) {E.PrintStackTrace () ؛ } إرجاع myobject ؛ }} هذه الطريقة هي أيضا كفاءة منخفضة للغاية. جميع الرموز في الطريقة مغلقة. تحتاج فقط إلى قفل رمز المفتاح. خطة الاستخدام الثالثة المتزامنة
حزمة com.weishiyao.learn.day8.singleton.ep3 ؛
الطبقة العامة myobject {private static myobject myobject ؛ Private MyObject () {} public static myobject getInstance () {// تأخير تحميل جرب {if (myObject! = null) {} آخر {// محاكاة بعض التحضير قبل إنشاء thread.sleep (2000) ؛ synchronized (myobject.class) {myObject = new myobject () ؛ }}} catch (interruptedException e) {E.PrintStackTrace () ؛ } إرجاع myobject ؛ }}يبدو أن هذا هو الحل الأفضل ، لكن بعد تشغيله ، وجدت أنه في الواقع غير آمن
نتيجة:
1224717057
971173439
1851889404
1224717057
1672864109
لماذا؟
على الرغم من أن العبارة التي تنشئ كائنًا مغلقًا ، إلا أن مؤشر ترابط واحد فقط يمكنه إكمال الإنشاء في وقت واحد ، بعد أن يأتي الخيط الأول لإنشاء كائن الكائن ، لا يزال بإمكان مؤشر الترابط الثاني أن يستمر في إنشائه ، لأننا قفلان عبارة الإنشاء فقط ، حل المشكلة هذا
حزمة com.weishiyao.learn.day8.singleton.ep3 ؛ الطبقة العامة myobject {private static myobject myobject ؛ private myobject () {} public static myobject getInstance () {// تأخير تحميل جرب {if (myObject! = null) {} آخر {// محاكاة بعض التحضير قبل إنشاء thread.sleep (2000) ؛ Synchronized (myobject.class) {if (myObject == null) {myObject = new myobject () ؛ }}}} catch (interruptedException e) {e.printStackTrace () ؛ } إرجاع myobject ؛ }}فقط أضف حكمًا آخر إلى القفل لضمان المفرد. هذه هي آلية التحقق المزدوجة DCL
النتائج كما يلي:
1224717057
1224717057
1224717057
1224717057
1224717057
3. استخدم فئات ثابتة مدمجة لتنفيذ حالات واحدة
الكود الرئيسي
package com.weishiyao.learn.day8.singleton.ep4 ؛ الطبقة العامة myobject {// طريقة الفئة الداخلية الفئة الثابتة الخاصة myobjectHandler {private static myobject myobject = new myobject () ؛ } public myobject () {} myobject getInstance () {return myobjectHandler.MyObject ؛ }} رمز فئة الموضوع
Package com.weishiyao.learn.day8.singleton.ep4 ؛ الفئة العامة myThread تمتد الموضوع {Override public void run () {system.out.println (myobject.getInstance (). hashcode ()) ؛ }} تشغيل الفصل
package com.weishiyao.learn.day8.singleton.ep4 ؛ الطبقة العامة تشغيل {public static void main (string [] args) {mythread t1 = new MyThread () ؛ mythread t2 = new MyThread () ؛ mythread t3 = new MyThread () ؛ mythread t4 = new MyThread () ؛ mythread t5 = new MyThread () ؛ t1.start () ؛ t2.start () ؛ t3.start () ؛ t4.start () ؛ t5.start () ؛ }}نتيجة
1851889404
1851889404
1851889404
1851889404
1851889404
من خلال الطبقات الثابتة الداخلية ، يتم الحصول على نمط المفرد الآمن الخيط
رابعا. تسلسل وفرط أنماط المفرد
يمكن للفئات الثابتة المدمجة تحقيق مشاكل سلامة مؤشرات الترابط ، ولكن إذا واجهت كائنات تسلسلية ، فإن النتيجة التي تم الحصول عليها باستخدام الطريقة الافتراضية لا تزال متعددة الحالات.
رمز myobject
package com.weishiyao.learn.day8.singleton.ep5 ؛ import java.io.serializable ؛ الطبقة العامة myobject الأدوات التسلسلية { / ** * * / private static Final Long SerialVersionuid = 888l ؛ // طريقة الفئة الداخلية الخاصة الفئة الثابتة myobjectHandler {private static myobject myobject = new myobject () ؛ } public myobject () {} myobject getInstance () {return myobjectHandler.MyObject ؛ } // myObject readResolve () {// system.out.println ("تم استدعاء طريقة ReadResolve!") ؛ عمل
package com.weishiyao.learn.day8.singleton.ep5 ؛ استيراد java.io.file java.io.objectOutputStream ؛ فئة عامة saveandread {public static void main (string [] args) {try {myObject myobject = myobject.getInstance () ؛ fileOutputStream fosref = new FileOutputStream (ملف جديد ("myobjectfile.txt")) ؛ ObjectOutputStream oosref = new ObjectOutputStream (fosref) ؛ ooosref.writeObject (myobject) ؛ OOSREF.CLOSE () ؛ fosref.close () ؛ System.out.println (myobject.hashCode ()) ؛ } catch (fileNotFoundException e) {e.printStackTrace () ؛ } catch (ioException e) {E.PrintStackTrace () ؛ } fileInputStream fisref ؛ حاول {fisref = new FileInputStream (ملف جديد ("myobjectfile.txt")) ؛ ObjectInputStream iOSREF = new 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
تثبت اثنين من الرموز المختلفين أنها ليست نفس الكائن. الحل ، أضف الرمز التالي
MyObject المحمي readResolve () {system.out.println ("تم استدعاء طريقة ReadResolve!") ؛ إرجاع myobjectHandler.myObject ؛ }استدعاء أثناء التخلص من التسلط ، يمكنك الحصول على نفس الكائن
system.out.println (myobject.readResolve (). hashcode ()) ؛
نتيجة
1255301379
تم استدعاء طريقة ReadResolve!
1255301379
يثبت نفس رمز hashcode أنه يتم الحصول على نفس الكائن
5. استخدم كتل التعليمات البرمجية الثابتة لتنفيذ حالة واحدة
يتم بالفعل تنفيذ الرمز في كتلة الكود الثابت عند استخدام الفصل ، بحيث يمكن استخدام ميزة الكود الثابت لتنفيذ وضع الربح البسيط.
فئة myobject
package com.weishiyao.learn.day8.singleton.ep6 ؛ public class myobject {private static myobject مثيل = null ؛ myobject الخاص () {super () ؛ } ثابت {مثيل = جديد myobject () ؛ } myobject getInstance () {return مثيل الإرجاع العام ؛ }} فئة الموضوع
package com.weishiyao.learn.day8.singleton.ep6 ؛ الفئة العامة myThread تمتد الموضوع {override public void run () {for (int i = 0 ؛ i <5 ؛ i ++) {system.out.println (myobject.getinstance (). hashcode ()) ؛ }}} تشغيل الفصل
package com.weishiyao.learn.day8.singleton.ep6 ؛ الطبقة العامة تشغيل {public static void main (string [] args) {mythread t1 = new MyThread () ؛ mythread t2 = new MyThread () ؛ mythread t3 = new MyThread () ؛ mythread t4 = new 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. استخدم أنواع بيانات التعداد لتنفيذ وضع Singleton
تتشابه خصائص تعداد التعداد وكتل الرمز الثابت. عند استخدام التعدادات ، سيتم استدعاء المنشئ تلقائيًا ويمكن أيضًا استخدامه لتنفيذ وضع Singleton.
فئة myobject
Package com.weishiyao.learn.day8.singleton.ep7 ؛ استيراد java.sql.connection ؛ استيراد java.sql.drivermanager ؛ استيراد java.sql.sqlexception ؛ التعداد العام myobject {connectionfactory ؛ اتصال الاتصال الخاص ؛ private myobject () {try {system.out.println ("تم استدعاء بناء myobject") ؛ url url = "jdbc: mysql: //172.16.221.19: 3306/weChat_1؟ useUnicode = true & farchoding = utf-8" ؛ اسم السلسلة = "الجذر" ؛ سلسلة كلمة المرور = "111111" ؛ string drivername = "com.mysql.jdbc.driver" ؛ class.forname (الاسم الحاجز) ؛ الاتصال = drivermanager.getConnection (url ، الاسم ، كلمة المرور) ؛ } catch (classNotFoundException e) {E.PrintStackTrace () ؛ } catch (sqlexception e) {E.PrintStackTrace () ؛ }} الاتصال العام getConnection () {return connection ؛ }} فئة الموضوع
package com.weishiyao.learn.day8.singleton.ep7 ؛ الفئة العامة myThread يمتد Thread {Override public void run () {for (int i = 0 ؛ i <5 ؛ i ++) {system.out.println (myobject.connectionFactory.getConnection (). }}} تشغيل الفصل
package com.weishiyao.learn.day8.singleton.ep7 ؛ الطبقة العامة تشغيل {public static void main (string [] args) {mythread t1 = new MyThread () ؛ mythread t2 = new MyThread () ؛ mythread t3 = new MyThread () ؛ mythread t4 = new 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
تعرض طريقة الكتابة أعلاه فئة التعداد ، التي تنتهك "مبدأ المسؤولية الفردية". يمكنك استخدام فئة لالتفاف التعداد.
package com.weishiyao.learn.day8.singleton.ep8 ؛ استيراد java.sql.connection ؛ استيراد java.sql.drivermanager ؛ import java.sql.sqlexception ؛ الطبقة العامة myobject {enum myenumsingleton {connectionfactory ؛ اتصال الاتصال الخاص ؛ myenumsingleton الخاص () {try {system.out.println ("تم استدعاء بناء myobject") ؛ url url = "jdbc: mysql: //172.16.221.19: 3306/weChat_1؟ useUnicode = true & farchoding = utf-8" ؛ اسم السلسلة = "الجذر" ؛ سلسلة كلمة المرور = "111111" ؛ string drivername = "com.mysql.jdbc.driver" ؛ class.forname (الاسم الحاجز) ؛ الاتصال = drivermanager.getConnection (url ، الاسم ، كلمة المرور) ؛ } catch (classNotFoundException e) {E.PrintStackTrace () ؛ } catch (sqlexception e) {E.PrintStackTrace () ؛ }} الاتصال العام getConnection () {return connection ؛ }} اتصال ثابت getConnection () {return myenumsingleton.connectionFactory.getConnection () ؛ }} تغيير رمز الموضوع
package com.weishiyao.learn.day8.singleton.ep8 ؛ الفئة العامة myThread يمتد thread {Override public void run () {for (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
يلخص ما ورد أعلاه المواقف والحلول المختلفة التي تمت مواجهتها عند الجمع بين وضع المصلحة الواحدة مع متعدد الخيوط ، بحيث يمكن مراجعتها عند استخدامها لاحقًا.