Semaphore هي فئة شائعة الاستخدام في حزمة JUC. إنه تطبيق وضع مشاركة AQS. يمكن أن تسمح لخيوط متعددة بالعمل على الموارد المشتركة في نفس الوقت ويمكن أن تتحكم بشكل فعال في عدد التزامن. يمكن أن تحقق مراقبة حركة المرور جيدة. يوفر Semaphore مفهومًا للترخيص ، والذي يمكن اعتباره تذكرة حافلة. فقط أولئك الذين يحصلون على التذكرة بنجاح يمكنهم الحصول على الحافلة. هناك عدد معين من التذاكر ومن المستحيل إصدارها دون قيود ، مما سيؤدي إلى زيادة الحمل الحافلة. لذلك عندما يتم إصدار التذكرة (الحافلة ممتلئة) ، يمكن للآخرين الانتظار فقط للقطار التالي. إذا خرج شخص ما من الحافلة في منتصف الطريق ، فسيكون موقفه مجانيًا ، لذلك إذا أراد الآخرون الدخول في الحافلة في هذا الوقت ، فيمكنهم الحصول على التذاكر مرة أخرى. يمكن تنفيذ تجمعات مختلفة باستخدام Semaphore. في نهاية هذه المقالة ، سنكتب تجمع اتصال قاعدة بيانات بسيط. أولاً ، دعونا نلقي نظرة على مُنشئ Semaphore.
// Constructor 1Public Semaphore (int permits) {sync = new nonfairsync (تصاريح) ؛} // constructor 2public smaphore (int تصاريح ، معرض منطقي) {sync = fair؟ New Fairsync (تصاريح): New Fairsync (تصاريح) ؛}يوفر Semaphore مُنشئين خالين من المعلمات ، ولكن لا يتم توفير أي مُنشئ خالية من المعلمات. يجب أن يمر كلا المنشئين في عدد أولي من التراخيص. سيتم الحصول على الإشارة التي تم إنشاؤها باستخدام مُنشئ 1 بطريقة غير فنية عند الحصول على الترخيص. يمكن أن يحدد استخدام المنشئ 2 طريقة الحصول على الترخيص من خلال المعلمات (عادلة أو غير عادلة). يوفر Semaphore بشكل أساسي نوعين من واجهات برمجة التطبيقات للعالم الخارجي ، والحصول على التراخيص وإصدار التراخيص. يتمثل الافتراضي في الحصول على ترخيص واحد وإصداره ، ويمكن أيضًا تمرير المعلمات للحصول على تراخيص متعددة وإصدارها في نفس الوقت. في هذه المقالة ، سوف نتحدث فقط عن موقف الحصول على ترخيص واحد وإطلاقه في كل مرة.
1. الحصول على ترخيص
// احصل على ترخيص (استجابة مقاطعة) الحصول على الفراغ العام الحصول على () رميات interruptedException {sync.acquiresharedInterruptilely (1) ؛} // احصل على ترخيص (لا يستجيب للمقاطعة) Sync.nonfairtryAcquireshared (1)> = 0 ؛} // حاول الحصول على ترخيص (فترة طويلة ، وحدة timeunit) رمي interruptedException {return sync.tryAcquiresharednoS (1 ، unt.tonanos (مهلة)) ؛}واجهة برمجة التطبيقات أعلاه هي عملية الحصول على الترخيص الافتراضية التي توفرها Semaphore. فقط الحصول على ترخيص واحد في وقت واحد هو أيضا وضع شائع في الحياة الحقيقية. بالإضافة إلى الجلب المباشر ، فإنه يوفر أيضًا محاولة للجلب. قد تقوم عملية الجلب المباشر بحظر الخيط بعد الفشل ، مع محاولة الجلب. تجدر الإشارة أيضًا إلى أن طريقة TryAcquire تستخدم لمحاولة الحصول عليها بطريقة غير عادلة. ما نستخدمه غالبًا في الأوقات العادية هو الحصول على ترخيص. دعونا نلقي نظرة على كيفية الحصول عليها. يمكنك أن ترى أن طريقة الاستحواذ تدعو مباشرة sync.acquiresharedInterrupturedrupruptireruptipluredruptive (1). هذه الطريقة هي الطريقة في AQS. تحدثنا ذات مرة عن مقالات سلسلة كود المصدر AQS. دعونا نراجعه مرة أخرى.
. } // 1. حاول الحصول على القفل if (tryacquireshared (arg) <0) {// 2. إذا فشل عملية الاستحواذ ، أدخل الطريقة doacquiresharedInterrupturedruptireruptireruptreructible (arg) ؛ }}الطريقة الأولى التي يتم الحصول عليها من قابلة للاتصال هي استدعاء طريقة TryAcquireshared لمحاولة الحصول عليها. TryAcquireshared هي طريقة مجردة في AQS. تنفذ الفئتان المشتقتان fairsync و nonfairsync منطق هذه الطريقة. تنفذ Fairsync منطق الاستحواذ العادل ، في حين أن Nonfairsync تنفذ منطق الاستحواذ على عدم التقدم.
يمتد Sync static static static static uccuredequeuedsynchronizer {// حاول الحصول على int النهائي nonfairtryacquireshared (int quivires) {for (؛؛) {// الحصول على التراخيص المتوفرة المتاحة = getState () ؛ // الحصول على التراخيص المتبقية int المتبقية = متوفرة - اكتساب ؛ // 1. إذا بقي أقل من 0 ، فابقى المتبقية مباشرة // 2. إذا تبقى أكبر من 0 ، قم بتحديث حالة التزامن أولاً ثم إرجاعها إذا (تبقى <0 || CompareAndSetState (متاح ، متبقي))) }}}}. nonfairsync (int التصاريح) {super (تصاريح) ؛ } // حاول الحصول على ترخيص محمي int tryacquireshared (int upplies) {return nonfairtryacquireshared (Acquires) ؛ }} // Fair Synchronizer Static Final Class Fairsync يمتد Sync {Private Static Final Long SerialVersionuid = 2014338818796000944l ؛ Fairsync (int تصاريح) {super (تصاريح) ؛ } // حاول الحصول على الترخيص المحمي int tryacquireshared (int quistirs) {for (؛؛) {// judge ما إذا كان هناك أي شخص أمام قائمة انتظار المزامنة إذا (hasqueuedpredecursors ()) {// إذا كان هناك أي ، يعود -1 ، مما يشير إلى أن محاولة الحصول على عودة فاشلة -1 ؛ } // الحصول على التراخيص المتاحة int متوفرة = getState () ؛ // الحصول على التراخيص المتبقية int المتبقية = متوفرة - اكتساب ؛ // 1. إذا بقي أقل من 0 ، عد مباشرة إلى المتبقية // 2. إذا كان المتبقي أكبر من 0 ، فسيتم تحديث حالة التزامن أولاً ثم يتم إرجاعها إلى المتبقية إذا (تبقى <0 || ContertSetState (متاح ، المتبقية)) {return المتبقية ؛ }}}}تجدر الإشارة هنا إلى أن طريقة TryAcquireshared ل nonfairsync تستدعي مباشرة طريقة غير المجالات ، والتي هي في مزامنة الفئة الأصل. يتمثل منطق قفل الاستحواذ غير القديم أولاً في إخراج حالة التزامن الحالية (تمثل الحالة المتزامنة عدد التراخيص) ، وطرح معلمة حالة التزامن الحالية. إذا لم تكن النتيجة أقل من 0 ، فقد ثبت أنه لا يزال هناك تراخيص متوفرة ، فسيتم تحديث قيمة حالة التزامن مباشرة باستخدام عملية CAS ، وأخيراً ، سيتم إرجاع قيمة النتيجة بغض النظر عما إذا كانت النتيجة أقل من 0. إن إرجاع الرقم السلبي يعني فشل الاستحواذ ، يعني الصفر أن الخيط الحالي قد تم الحصول عليه بنجاح ولكن لا يمكن الحصول على مؤشر الترابط اللاحق بعد الآن ، والرقم الموجب يعني أن الخيط الحالي يتم الحصول عليه بنجاح ويمكن أيضًا الحصول على مؤشر الترابط اللاحق. دعونا نلقي نظرة على رمز طريقة المكتسبة.
. } // 1. حاول الحصول على رقم القفل // السلبي: يشير إلى أن الاستحواذ فشل // قيمة صفرية: يشير إلى أن مؤشر الترابط الحالي قد تم الحصول عليه بنجاح ، ولكن لم يعد بإمكان مؤشر الترابط اللاحق الحصول على // رقم إيجابي: يشير إلى أن الخيط الحالي يتم الحصول عليه بنجاح ، ويمكن أن يحصل مؤشر الترابط اللاحق أيضًا على النجاح (TryAcquireshared (arg) <0) {// 2. إذا فشل عملية الاستحواذ ، أدخل الطريقة doacquiresharedInterrupturedruptireruptireruptreructible (arg) ؛ }}إذا كانت المتبقية المتبقية أقل من 0 ، فهذا يعني أن عملية الاستحواذ فشلت. لذلك ، فإن TryAcquireshared (Arg) <0 صحيح ، لذلك سيتم استدعاء طريقة doacquiresharedInterruptively. عندما تحدثنا عن AQS ، سوف يلف الخيط الحالي في عقدة ويضعه في ذيل قائمة انتظار المزامنة ، ومن الممكن تعليق الخيط. هذا هو أيضًا السبب في أن مؤشرات الترابط ستصطف في طوابير وحظرها عند البقاء أقل من 0. إذا تم إرجاع المتبقية> = 0 يعني أن الخيط الحالي قد تم الحصول عليه بنجاح. لذلك ، فإن TryAcquireshared (Arg) <0 عبارة عن flase ، وبالتالي لن يتم استدعاء طريقة doacquiresharedInterruptluredruptluredruptible لحظر الخيط الحالي. ما سبق هو المنطق الكامل للاستحواذ غير العادل. عند الاستحواذ العادل ، تحتاج فقط إلى استدعاء طريقة HasqueuedPredecursors قبل ذلك لتحديد ما إذا كان شخص ما يصطف في قائمة انتظار المزامنة. إذا كان الأمر كذلك ، فإن الإرجاع -1 يشير مباشرة إلى أن عملية الاستحواذ قد فشلت ، وإلا فإن الخطوات التالية تستمر على أنها عملية استحواذ غير عادلة.
2. حرر الترخيص
// قم بإصدار ترخيص الإصدار الفراغ العام () {sync.releaseshared (1) ؛}استدعاء طريقة الإصدار هو إصدار ترخيص. تشغيلها بسيط للغاية ، لذلك نسمي طريقة RELEASESHARED من AQS. دعونا نلقي نظرة على هذه الطريقة.
// إطلاق قفل عملية (الوضع المشترك) النهائي العام النهائي RELEASESHARED (int arg) {// 1. حاول إطلاق القفل إذا (tryreleaseshared (arg)) {// 2. إذا كان الإصدار ناجحًا ، استيقظ مؤشرات ترابط أخرى doreleaseshared () ؛ العودة صحيح. } إرجاع خطأ ؛}تستدعي طريقة RELEASESSERADE من AQS أولاً طريقة Tryreleaseshared لمحاولة تحرير القفل. منطق تنفيذ هذه الطريقة هو في مزامنة الفئة الفرعية.
ملخص Sync Sync Static ExtructiqueuedSynchronizer {... // حاول إطلاق العملية المحمية Boolean Tryreleaseshared (int reease) {for (؛؛) {// الحصول على حالة التزامن الحالية int current = getState () ؛ // بالإضافة إلى حالة التزامن الحالية كما يلي int next = current + reeases ؛ // إذا كانت نتيجة الإضافة أقل من حالة المزامنة الحالية ، فسيتم الإبلاغ عن خطأ إذا (التالي <الحالي) {رمي خطأ جديد ("تم تجاوز عدد تصريح الحد الأقصى") ؛ } // تحديث قيمة حالة التزامن في وضع CAS ، وإرجاع TRUE إذا كان التحديث ناجحًا ، وإلا استمر في حلقات إذا (ConperteAndSetState (Current ، Next)) {return true ؛ }}} ...}يمكنك أن ترى أن طريقة tryreleaseshared تستخدم حلقة لتدور. أولاً ، احصل على حالة التزامن ، وأضف المعلمات الواردة ، ثم قم بتحديث حالة التزامن في CAS. إذا كان التحديث ناجحًا ، فأعد صواب وقفز خارج الطريقة. خلاف ذلك ، ستستمر الحلقة حتى تنجح. هذه هي عملية إطلاق الإشارة إلى الترخيص.
3. اكتب تجمع اتصال يدويًا
رمز الإشارة ليس معقدًا للغاية. العملية الشائعة الاستخدام هي الحصول على ترخيص وإصداره. منطق تنفيذ هذه العمليات بسيط نسبيًا ، لكن هذا لا يعيق التطبيق الواسع النطاق لـ Semaphore. بعد ذلك ، سوف نستخدم Semaphore لتنفيذ تجمع اتصال قاعدة بيانات بسيط. من خلال هذا المثال ، نأمل أن يكون لدى القراء فهم أعمق لاستخدام Semaphore.
الفئة العامة ConnectPool {// Connection Pool Size Private Int Size ؛ // Connection Collection Collection Private Connect [] Connects ؛ // علامة اتصال العلم الخاص بملح منطقي [] connectFlag ؛ // العدد المتبقي من الاتصالات المتاحة المتقلبة المتوفرة ؛ // semaphore semaphore semaphore ؛ // Constructor Public ConnectPool (int size) {this.size = size ؛ this.available = الحجم ؛ semaphore = semaphore جديد (الحجم ، صحيح) ؛ Connects = New Connect [size] ؛ connectFlag = جديد منطقي [الحجم] ؛ initConnects () ؛ } // تهيئة الاتصال الخاص بالتوصيل الخاص () {// إنشاء عدد محدد من اتصالات قاعدة البيانات لـ (int i = 0 ؛ i <this.size ؛ i ++) {connects [i] = new connect () ؛ }} // الحصول على اتصال قاعدة البيانات الخاصة connect synchronized connect getConnect () {for (int i = 0 ؛ i <connectflag.length ؛ i ++) {// نقل المجموعة للعثور على اتصالات غير مستخدمة إذا (! connectFlag [i]) {// قم بتعيين اتصال في استخدام connectflag [i] = true ؛ // طرح عدد الاتصالات المتاحة المتاحة-؛ System.out.println ("【"+thread.currentThRead (). getName ()+"】 للحصول على عدد الاتصالات المتبقية:"+متاح) ؛ // إرجاع Connect Reference Return Connects [i] ؛ }} الإرجاع null ؛ } // الحصول على اتصال عام Connect OpenConnect () رميات interruptedException {// الحصول على ترخيص semaphore.acquire () ؛ // الحصول على اتصال قاعدة البيانات return getConnect () ؛ } // قم بإصدار اتصال باطل متزامن عام (Connect Connect) {for (int i = 0 ؛ i <this.size ؛ i ++) {if (connect == connects [i]) {// قم بتعيين الاتصال بعدم استخدام connectflag [i] = false ؛ // إضافة 1 رقم اتصال متاح ؛ system.out.println ("【"+thread.currentTherAd (). getName ()+"] لإصدار رقم الاتصال المتبقي:"+متاح) ؛ // إصدار ترخيص Semaphore.Release () ؛ }}} // إضافة عدد من الاتصالات المتاحة العامة المتاحة () {return Available ؛ }}رمز الاختبار:
يمتد TestTreshread من الفئة العامة Thread {Private Static ConnectPool Pool = New ConnectPool (3) ؛ Override public void run () {try {connect connect = pool.openconnect () ؛ thread.sleep (100) ؛ // خذ break pool.release (connect) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }} public static void main (string [] args) {for (int i = 0 ؛ i <10 ؛ i ++) {new testthread (). start () ؛ }}}نتائج الاختبار:
نستخدم صفيفًا لتخزين الإشارات إلى اتصالات قاعدة البيانات. عند تهيئة تجمع الاتصال ، سنقوم باستدعاء طريقة LIGNCONNECTs لإنشاء عدد محدد من اتصالات قاعدة البيانات وتخزين مراجعها في المصفوفة. بالإضافة إلى ذلك ، هناك صفيف من نفس الحجم لتسجيل ما إذا كان الاتصال متاحًا. عندما يطلب مؤشر ترابط خارجي الحصول على اتصال ، اتصل أولاً بالطريقة smaphore.acquire () للحصول على ترخيص ، ثم قم بتعيين حالة الاتصال قيد الاستخدام ، وأخيراً إرجاع المرجع إلى الاتصال. يتم تحديد عدد التراخيص من خلال المعلمات التي تم تمريرها أثناء البناء. يتم تخفيض عدد التراخيص بمقدار 1 في كل مرة يتم فيها استدعاء طريقة semaphore.Acquire (). عندما يتم تقليل الرقم إلى 0 ، فهذا يعني أنه لا يوجد اتصال متاح. في هذا الوقت ، إذا حصلت عليه المواضيع الأخرى مرة أخرى ، فسيتم حظره. عندما يطلق مؤشر ترابط الاتصال ، سيتم استدعاء semaphore.rlease () لإصدار الترخيص. في هذا الوقت ، سيزداد إجمالي عدد التراخيص مرة أخرى ، مما يعني أن عدد الاتصالات المتاحة قد زاد. ثم يستيقظ الخيط المحظور مسبقًا ويستمر في الحصول على الاتصال. في هذا الوقت ، يمكنك الحصول على الاتصال بنجاح من خلال الحصول عليه مرة أخرى. في مثال الاختبار ، تتم تهيئة مجموعة اتصال من 3 اتصالات. يمكننا أن نرى من نتائج الاختبار أنه كلما حصل مؤشر ترابط على اتصال ، سيتم تقليل عدد الاتصالات المتبقية بمقدار 1. عندما ينخفض مؤشر الترابط إلى 0 ، لم يعد بإمكان مؤشرات الترابط الحصول عليه. في هذا الوقت ، يجب أن تنتظر حتى يتم إصدار سلسلة رسائل قبل الاستمرار في الحصول عليها. يمكنك أن ترى أن عدد الاتصالات المتبقية يتغير دائمًا بين 0 و 3 ، مما يعني أن اختبارنا كان ناجحًا.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.