لقد اكتشفت موقفًا أثناء تصحيح أخطاء التعليمات البرمجية، أي عندما تحققت من اتصالات memcached، وجدت أنه تم الاحتفاظ بها دائمًا عند حوالي 100. بالطبع، يبدو أن هذا لا يمثل مشكلة، لأن memcached لديه 1024 اتصالًا بشكل افتراضي. ولكن ما أفكر فيه هو سبب وجود 100 أو نحو ذلك. نظرًا لأن memcachedclient الخاص بي يتم إنشاؤه في الوضع الفردي، فقد قمت بتحديد فئة memcachedClientFactory. الكود الرئيسي هو كما يلي:
خاص MemcachedClientFactory(){
}
خاص MemcachedClientFactory(MemcachedConnectionBuilder memcachedConnectionBuilder, String servers){
this.memcachedConnectionBuilder= memcachedConnectionBuilder;
this.servers=servers;
}
MemcachedClient العام الثابت createClient(){
إذا (memcachedClient==null){
this.memcahcedClien= new MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));
}
إرجاع this.memcahcedClient;
}
}
}
عودة إلى السؤال الأصلي، لماذا يوجد أكثر من 100 اتصال؟
هل يمكن لطريقة الكتابة المذكورة أعلاه ضمان إنشاء اتصال واحد فقط؟ من الواضح لا، لماذا؟ التزامن متعدد الخيوط! تكمن المشكلة هنا. عندما تدخل عدة سلاسل رسائل إلى طريقة createClient() في نفس الوقت، وتقرر جميعًا أن memcachedClient فارغ، يتم إنشاء اتصالات متعددة. ها، تم العثور على المشكلة.
يحسن:
هذا جيد، التغيير بسيط جدًا. لا توجد مشكلة في البرنامج، ويضمن وجود اتصال واحد فقط.
لكن إذا وضعنا هذه المشكلة جانبًا، يمكننا الاستمرار في التفكير بعمق حول كيفية حل مشكلة التزامن في الوضع الفردي.
اسمحوا لي أن ألخص، هناك ثلاث طرق تقريبًا لحل مشكلة التزامن في الوضع الفردي:
1. لا تستخدم إنشاء مثيل مؤجل، ولكن استخدم إنشاء مثيل مبكر.
أي أنه يتم إعادة كتابة البرنامج على النحو التالي:
getInstance المفردة العامة الثابتة () {
مثيل الإرجاع؛
}
}
عند القيام بذلك، يقوم jvm بإنشاء المثيل فورًا عند تحميل الفصل، لذا فإن فرضية ذلك هي أن عبء إنشاء المثيل ليس كبيرًا، ولا أفكر كثيرًا في الأداء، ونؤكد أن المثيل سيكون بالتأكيد يمكن استخدامها. في الواقع، يمكن أيضًا استخدام الكود السابق بهذه الطريقة:
خاص MemcachedClientFactory(){
}
خاص MemcachedClientFactory(MemcachedConnectionBuilder memcachedConnectionBuilder, String servers){
this.memcachedConnectionBuilder= memcachedConnectionBuilder;
this.servers=servers;
}
MemcachedClient العام الثابت createClient(){
إرجاع this.memcahcedClient;
}
}
}
ومع ذلك، يبدو أنه لا توجد مشكلة، ولكن هناك خطر خفي، وهو أنه بمجرد قيام شخص ما باستدعاء طريقة memcachedClient.shutdown() عن طريق الخطأ، فلن يتمكن البرنامج بأكمله من إنشاء memcachedClient جديد. بالطبع هذه حالة متطرفة، ولكن من أجل قوة الكود، يمكن تغييرها إلى:
2. ما عليك سوى استخدام الكلمة الأساسية المتزامنة.
يمكن أن يؤدي القيام بذلك إلى ضمان حدوث مشكلات في المزامنة، ولكننا نعلم أن الحمل الزائد لاستخدام المزامنة مرتفع جدًا وسيؤثر بشكل خطير على الأداء، لذا فإن فرضية استخدام هذا هو تأكيد أنك لن تستدعي هذه الطريقة بشكل متكرر، أو أن الحمل الزائد للإنشاء هذه الحالة لن تكون كبيرة. ما إذا كان يمكن تحسينه، انظر أدناه.
3. استخدم "قفل التحقق المزدوج" واستخدم المزامنة في getInstance
سينجلتون خاص () {}؛
getInstance المفردة العامة الثابتة () {
إذا (المثيل==فارغ){
متزامن (Singleton.class) {
إذا (المثيل==فارغ){
مثيل = مفرد جديد () ؛
}
}
}
مثيل الإرجاع؛
}
}