الأمان في التداخل في فئات الوحدة في جافا

الفرادة (Singleton) هو واحد من أكثر أنماط التصميم الإنشائي استخدامًا لتقييد الكائن الذي تقوم بإنشائه التطبيقات. إذا كنت تستخدمه في بيئة متعددة الخيوط، فإن سلامة الخيط (thread-safety) لفئة الفرادة تكون مهمة جدًا. في تطبيقات العالم الحقيقي، الموارد مثل اتصالات قواعد البيانات أو أنظمة المعلومات التنظيمية (EIS) محدودة ويجب استخدامها بحكمة لتجنب أي نقص في الموارد. لتحقيق ذلك، يمكننا تنفيذ نمط تصميم الفرادة. يمكننا إنشاء فئة تغليف للمورد وتقييد عدد الكائنات التي يمكن إنشاؤها في وقت التشغيل إلى واحدة.

فرادة آمنة للخيوط في جافا

بشكل عام، نتبع الخطوات التالية لإنشاء فئة فرادة:

  1. إنشاء البناء الخاص لتجنب إنشاء أي كائن جديد بواسطة مشغل new.
  2. تعلن عن نسخة ثابتة خاصة من نفس الفئة.
  3. توفير طريقة ثابتة عامة ستعيد متغير الفئة المفردة. إذا لم يتم تهيئة المتغير، فقم بتهيئته، وإلا فقط ارجع متغير الفئة.

باستخدام الخطوات المذكورة أعلاه، قمت بإنشاء فئة مفردة تبدو كما يلي. ASingleton.java

package com.journaldev.designpatterns;

public class ASingleton {

	private static ASingleton instance = null;

	private ASingleton() {
	}

	public static ASingleton getInstance() {
		if (instance == null) {
			instance = new ASingleton();
		}
		return instance;
	}

}

في الكود أعلاه، طريقة getInstance() ليست آمنة من حيث الأسلاك. يمكن لعدة أسلاك الوصول إليه في نفس الوقت. بالنسبة للأسلاك القليلة الأولى عندما لا يتم تهيئة متغير الفئة، يمكن لعدة أسلاك الدخول إلى حلقة الشرط وإنشاء عدة مثيلات. ستؤدي هذه الحالة إلى كسر تنفيذ الفئة المفردة لدينا.

كيفية تحقيق أمان الأسلاك في فئة Singleton؟

هناك ثلاث طرق يمكننا من خلالها تحقيق أمان الأسلاك.

  1. إنشاء متغير الفئة أثناء تحميل الفئة.
    الإيجابيات:
  • أمان الأسلاك بدون تزامن
  • سهولة التنفيذ

السلبيات:

  • إنشاء مبكر للمورد قد لا يتم استخدامه في التطبيق.
  • لا يمكن لتطبيق العميل تمرير أي وسيطة، لذلك لا يمكننا إعادة استخدامه. على سبيل المثال، يمكن أن يكون لدينا فئة مفردة عامة لاتصال قاعدة البيانات حيث يقدم تطبيق العميل خصائص خادم قاعدة البيانات.
  1. تزامن طريقة getInstance().
    الإيجابيات:
  • تضمن أمان الخيط.
  • يمكن لتطبيق العميل تمرير المعلمات
  • تحقيق التهيئة الكسولة

السلبيات:

  • أداء بطيء بسبب فوقية القفل.
  • تزامن غير ضروري غير مطلوب بمجرد تهيئة متغير الفرضية.
  1. استخدم كتلة متزامنة داخل حلقة if ومتغير متقلب
    الإيجابيات:
  • تضمن أمان الخيط
  • يمكن لتطبيق العميل تمرير الوسائط
  • تحقيق التهيئة الكسولة
  • تكلفة التزامن هي دنيا وتطبق فقط لأول عدة خيوط عندما يكون المتغير معدومًا.

السلبيات:

  • شرط if إضافي

عند النظر إلى جميع الطرق الثلاث لتحقيق أمان الخيط ، أعتقد أن الطريقة الثالثة هي الخيار الأفضل. في هذه الحالة ، ستبدو الفئة المعدلة هكذا:

package com.journaldev.designpatterns;

public class ASingleton {

	private static volatile ASingleton instance;
	private static Object mutex = new Object();

	private ASingleton() {
	}

	public static ASingleton getInstance() {
		ASingleton result = instance;
		if (result == null) {
			synchronized (mutex) {
				result = instance;
				if (result == null)
					instance = result = new ASingleton();
			}
		}
		return result;
	}

}

يبدو أن المتغير المحلي result غير ضروري. ولكن ، هناك لتحسين أداء كودنا. في الحالات التي تكون فيها الفئة مهيأة بالفعل (في معظم الأحيان) ، يتم الوصول إلى الحقل المتقلب مرة واحدة فقط (بسبب “return result;” بدلاً من “return instance;”). يمكن أن يحسن هذا الأداء العام للطريقة بنسبة تصل إلى 25 في المئة. إذا كنت تعتقد أن هناك وسائل أفضل لتحقيق ذلك أو إذا تم التنازل عن أمان الخيط في التنفيذ أعلاه ، يرجى التعليق ومشاركته مع الجميع.

نصيحة إضافية

السلسلة ليست مرشحة جيدة للاستخدام مع الكلمة المفتاحية synchronized. هذا لأنها تخزن في بركة السلاسل ولا نريد قفل سلسلة قد تكون قيد الاستخدام بواسطة قطعة أخرى من الشيفرة. لذلك أستخدم متغيرًا من نوع Object. تعرف على المزيد حول التزامن وسلامة التداول في جافا.

يمكنك التحقق من المزيد من أمثلة جافا في مستودعنا على جيتهاب.

Source:
https://www.digitalocean.com/community/tutorials/thread-safety-in-java-singleton-classes