هل يحل هذا الرمز مشكلة القفل المزدوج في Java؟
-
12-11-2019 - |
سؤال
هل يحل هذا الرمز مشكلة القفل المزدوج في Java؟
public class DBAccessService() {
private static DBAccessService INSTANCE;
private DBAccessService() {}
public static DBAccessService getInstance() {
if (INSTANCE != null) {
return INSTANCE;
}
return createInstance();
}
private static synchronized DBAccessService createInstance() {
if (INSTANCE != null) {
return INSTANCE;
}
DBAccessService instance = new DBAccessService();
INSTANCE = instance;
return INSTANCE;
}
}
هناك جانبان يجب الانتباه إليهما:
getInstance()
يكون لا متزامنة، لذلك بعد تهيئة INSTANCE لا توجد تكلفة للمزامنةcreateInstance()
تتم مزامنة
إذن السؤال هو:هل هذا الرمز لديه أي مشاكل؟هل هو قانوني و دائماً خيط آمن؟
المحلول
لحل هذا السؤال بالذات جافا التزامن في الممارسة العملية (كتبه الفريق الذي كتب مكتبة java.util.concurrent بشكل أساسي) يوصي بـ لغة فئة حامل التهيئة الكسول (صفحة 348 في نسختي، القائمة 16.6، وليس 16.7)
@ThreadSafe
public class DBAccessServiceFactory {
private static class ResourceHolder {
public static DBAccessService INSTANCE = new DBAccessService();
}
public static DBAccessService getResource() {
return ResourceHolder.INSTANCE;
}
}
هذا دائمًا قانوني وآمن للخيط.أنا لست خبيرا، لذلك لا أستطيع أن أقول أن هذا أفضل من الكود الخاص بك.ومع ذلك، نظرًا لأنه النمط الذي أوصى به دوج ليا وجوشوا بلوخ، سأستخدمه دائمًا فوق الكود الذي اخترعته أنت أو أنا، لأنه من السهل جدًا ارتكاب الأخطاء (كما يتضح من عدد الإجابات الخاطئة على هذا السؤال ).
وفيما يتعلق بالمسألة المتقلبة يقولون:
التغييرات اللاحقة في JMM (Java 5.0 والإصدارات الأحدث) مكنت DCL من العمل إذا أصبح المورد متقلبًا ...ومع ذلك، يقدم مصطلح حامل التهيئة البطيء نفس الفوائد ويسهل فهمه.
نصائح أخرى
عليك أن تعلن INSTANCE
مثل volatile
لكي تعمل:
private static volatile DBAccessService INSTANCE;
لاحظ أنه يعمل فقط مع Java 5 والإصدارات الأحدث.يرى إعلان "التأمين المزدوج معطل"..
في هذه المقالة يُزعم أن "التسجيل المزدوج" لا يمثل مشكلة إذا كنت تستخدم فئة Singleton منفصلة:
public class DBAccessHelperSingleton {
public static DBAccessHelper instance = new DBAccessHelper();
}
وله نفس الميزة:لا يتم إنشاء مثيل للحقل قبل أن يكون مرجعًا في المرة الأولى.
كما ذكرنا سابقًا، إذا كنت تريد الاحتفاظ كما هو الحال لديك volatile
مفقود في حالة استهداف JDK فقط >= 5.
إنها ليست آمنة للخيط، يرجى التحقق من ممتاز شرط.ليس هناك ما يضمن أن مؤشر ترابط واحد سوف يرى مثيل DbAccessService الذي تمت تهيئته بالكامل.لماذا لا تستخدم بسيطة
public class DBAccessService() {
public static DBAccessService INSTANCE = new DBAccessService();
}
تبدو على ما يرام.إذا استدعى خيطان getInstance() ولم تتم تهيئة INSTANCE، فسيتمكن مؤشر ترابط واحد فقط من متابعة createInstance() وسيرى الثاني بالفعل أن المثيل ليس فارغًا.
الشيء الوحيد هو volatile
الكلمة الأساسية لـ INSTANCE.وإلا يمكن لجافا تخزينها مؤقتًا.