سؤال

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

أسئلتي للمجتمع هي:

ما هي حالة السباق؟كيف تكتشفهم؟كيف يمكنك التعامل معها؟وأخيرا، كيف يمكنك منع حدوثها؟

هل كانت مفيدة؟

المحلول

تحدث حالة السباق عندما يتمكن خيطان أو أكثر من الوصول إلى البيانات المشتركة ويحاولون تغييرها في نفس الوقت.نظرًا لأن خوارزمية جدولة سلاسل الرسائل يمكنها التبديل بين سلاسل الرسائل في أي وقت، فإنك لا تعرف الترتيب الذي ستحاول به سلاسل الرسائل الوصول إلى البيانات المشتركة.لذلك، تعتمد نتيجة التغيير في البيانات على خوارزمية جدولة الخيط، أي.كلا الموضوعين "يتسابقان" للوصول إلى/تغيير البيانات.

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

if (x == 5) // The "Check"
{
   y = x * 2; // The "Act"

   // If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
   // y will not be equal to 10.
}

النقطة المهمة هي أن y يمكن أن يكون 10، أو يمكن أن يكون أي شيء، اعتمادًا على ما إذا كان مؤشر ترابط آخر قد قام بتغيير x بين الفحص والتصرف.ليس لديك طريقة حقيقية للمعرفة.

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

// Obtain lock for x
if (x == 5)
{
   y = x * 2; // Now, nothing can change x until the lock is released. 
              // Therefore y = 10
}
// release lock for x

نصائح أخرى

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

خذ هذا المثال:

for ( int i = 0; i < 10000000; i++ )
{
   x = x + 1; 
}

إذا كان لديك 5 سلاسل تنفذ هذا الرمز مرة واحدة، فلن تصل قيمة x في النهاية إلى 50,000,000.في الواقع سوف يختلف مع كل شوط.

وذلك لأنه لكي يقوم كل خيط بزيادة قيمة x، عليه القيام بما يلي:(مبسط، واضح)

Retrieve the value of x
Add 1 to this value
Store this value to x

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

لنفترض أن الخيط يسترد قيمة x، لكنه لم يخزنها بعد.يمكن لموضوع آخر أيضًا استرداد ملف نفس قيمة x (لأنه لم يغيره أي مؤشر ترابط حتى الآن) وبعد ذلك سيقوم كلاهما بتخزين ملف نفس القيمة (x+1) تعود إلى x!

مثال:

Thread 1: reads x, value is 7
Thread 1: add 1 to x, value is now 8
Thread 2: reads x, القيمة هي 7
Thread 1: stores 8 in x
Thread 2: adds 1 to x, value is now 8
Thread 2: مخازن 8 في x

يمكن تجنب ظروف السباق من خلال استخدام نوع ما قفل الآلية قبل الكود الذي يصل إلى المورد المشترك:

for ( int i = 0; i < 10000000; i++ )
{
   //lock x
   x = x + 1; 
   //unlock x
}

هنا، الجواب يخرج بـ 50,000,000 في كل مرة.

للمزيد عن القفل، ابحث عن:كائن المزامنة، الإشارة، القسم الحرج، المورد المشترك.

ما هي حالة السباق؟

كنت تخطط للذهاب إلى فيلم في الساعة 5 مساء.يمكنك الاستفسار عن توفر التذاكر في الساعة 4 مساءً.يقول الممثل أنهم متاحون.يمكنك الاسترخاء والوصول إلى نافذة التذاكر قبل 5 دقائق من العرض.أنا متأكد من أنه يمكنك تخمين ما سيحدث:إنه منزل كامل.المشكلة هنا كانت في المدة بين الشيك والإجراء.لقد استفسرت في الرابعة وتصرفت في الخامسة.وفي هذه الأثناء، حصل شخص آخر على التذاكر.هذه حالة سباق - على وجه التحديد سيناريو "التحقق ثم التصرف" لظروف السباق.

كيف تكتشفهم؟

مراجعة الكود الديني، واختبارات الوحدات متعددة الخيوط.لا يوجد اختصار.هناك عدد قليل من المكونات الإضافية لـ Eclipse التي تظهر في هذا الشأن، ولكن لا يوجد شيء مستقر حتى الآن.

كيف تتعامل معهم وتمنعهم؟

أفضل شيء هو إنشاء وظائف خالية من الآثار الجانبية وعديمة الحالة، واستخدام العناصر الثابتة قدر الإمكان.ولكن هذا ليس ممكنا دائما.لذا فإن استخدام java.util.concurrent.atomic وهياكل البيانات المتزامنة والمزامنة المناسبة والتزامن القائم على الممثل سيساعد.

أفضل مورد للتزامن هو JCIP.يمكنك أيضًا الحصول على المزيد تفاصيل عن الشرح أعلاه هنا.

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

يحدث سباق البيانات عندما تصل تعليماتان إلى نفس موقع الذاكرة، وتكون إحدى عمليات الوصول هذه على الأقل عبارة عن عملية كتابة ولا يوجد يحدث قبل الطلب من بين هذه الوصولاتالآن ما يشكل حدثًا قبل الطلب يخضع لكثير من الجدل، ولكن بشكل عام، تؤدي أزواج ulock-lock على نفس متغير القفل وأزواج إشارة الانتظار على نفس متغير الشرط إلى حدوث أمر قبل الأمر.

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

العديد من حالات السباق يمكن أن تكون (في الواقع) ناجمة عن سباقات البيانات، ولكن هذا ليس ضروريًا.وفي واقع الأمر، فإن سباقات البيانات وظروف السباق ليست شرطًا ضروريًا أو كافيًا لبعضها البعض. هذا يشرح منشور المدونة أيضًا الفرق جيدًا، مع مثال بسيط للمعاملة المصرفية.وهنا آخر بسيط مثال وهذا ما يفسر الفرق.

والآن بعد أن حددنا المصطلحات، دعونا نحاول الإجابة على السؤال الأصلي.

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

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

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

نوع من التعريف الكنسي هو "عندما يصل خيطان إلى نفس الموقع في الذاكرة في نفس الوقت، ويكون أحد عمليات الوصول على الأقل عبارة عن عملية كتابة". في هذه الحالة، قد يحصل مؤشر الترابط "القارئ" على القيمة القديمة أو القيمة الجديدة، اعتمادًا على الخيط الذي "يفوز بالسباق". وهذا ليس خطأً دائمًا - في الواقع، تقوم بعض الخوارزميات ذات المستوى المنخفض جدًا بذلك الغرض - ولكن يجب تجنبه بشكل عام.يقدم @ Steve Gury مثالاً جيدًا على الحالات التي قد تكون فيها مشكلة.

حالة السباق هي نوع من الأخطاء التي تحدث فقط مع ظروف زمنية معينة.

مثال:تخيل أن لديك خيطين، A وB.

في الموضوع أ:

if( object.a != 0 )
    object.avg = total / object.a

في الموضوع ب:

object.a = 0

إذا تم استباق مؤشر الترابط A مباشرة بعد التحقق من أن الكائن.a ليس فارغًا، فسيقوم B بذلك a = 0, ، وعندما يحصل الخيط A على المعالج، فإنه سيقوم بإجراء "القسمة على صفر".

يحدث هذا الخطأ فقط عندما يتم استباق مؤشر الترابط A مباشرة بعد عبارة if، وهو أمر نادر جدًا، ولكنه يمكن أن يحدث.

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

تعتمد أدوات منع حالات السباق على اللغة ونظام التشغيل، ولكن بعض الأدوات الشائعة هي كائنات المزامنة والأقسام المهمة والإشارات.تعتبر Mutexes جيدة عندما تريد التأكد من أنك الشخص الوحيد الذي يفعل شيئًا ما.تكون الإشارات جيدة عندما تريد التأكد من أن شخصًا آخر قد انتهى من القيام بشيء ما.يمكن أن يساعد تقليل الموارد المشتركة أيضًا في منع السلوكيات غير المتوقعة

قد يكون اكتشاف ظروف السباق أمرًا صعبًا، ولكن هناك بعض العلامات.التعليمات البرمجية التي تعتمد بشكل كبير على فترات النوم تكون عرضة لظروف السباق، لذا تحقق أولاً من وجود مكالمات للنوم في التعليمات البرمجية المتأثرة.يمكن أيضًا استخدام إضافة فترات نوم طويلة بشكل خاص لتصحيح الأخطاء لمحاولة فرض ترتيب معين للأحداث.يمكن أن يكون هذا مفيدًا لإعادة إنتاج السلوك، ومعرفة ما إذا كان بإمكانك جعله يختفي عن طريق تغيير توقيت الأشياء، ولاختبار الحلول المطبقة.يجب إزالة Sleeps بعد التصحيح.

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

لا ترتبط حالة السباق بالبرمجيات فحسب، بل تتعلق أيضًا بالأجهزة أيضًا.في الواقع تمت صياغة هذا المصطلح في البداية من قبل صناعة الأجهزة.

وفق ويكيبيديا:

المصطلح ينشأ مع فكرة إشارتان تتسابقان مع بعضهما البعض ل التأثير على الإخراج أولاً.

حالة السباق في الدائرة المنطقية:

enter image description here

أخذت صناعة البرمجيات هذا المصطلح دون تعديل، مما يجعل فهمه صعبًا بعض الشيء.

تحتاج إلى إجراء بعض الاستبدال لتعيينه لعالم البرامج:

  • "إشارتان" => "خيطان"/"عمليتان"
  • "التأثير على المخرجات" => "التأثير على بعض الحالات المشتركة"

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

حالة السباق هي حالة برمجة متزامنة حيث يتنافس خيطان أو عمليتان متزامنتان على مورد وتعتمد الحالة النهائية الناتجة على من يحصل على المورد أولاً.

لقد نشرت مايكروسوفت فعلا مفصلة حقا شرط في هذه المسألة المتعلقة بظروف السباق والجمود.سيكون الملخص الأكثر تلخيصًا منه هو فقرة العنوان:

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

ما هي حالة السباق؟

الموقف الذي تعتمد فيه العملية بشكل حاسم على تسلسل أو توقيت الأحداث الأخرى.

على سبيل المثال ، المعالج أ والمعالج ب كلا الاحتياجات نفس الموارد لتنفيذها.

كيف تكتشفهم؟

هناك أدوات لاكتشاف حالة السباق تلقائيًا:

كيف يمكنك التعامل معها؟

يمكن التعامل مع حالة السباق بواسطة موتيكس أو الإشارات.إنها بمثابة قفل يسمح لعملية الحصول على مورد بناءً على متطلبات معينة لمنع حالة السباق.

كيف يمكنك منع حدوثها؟

هناك طرق مختلفة لمنع حالة السباق، مثل تجنب القسم الحرج.

  1. لا توجد عمليتان في وقت واحد داخل مناطقهما الحرجة.(استبعاد متبادل)
  2. لا يتم وضع أي افتراضات حول السرعات أو عدد وحدات المعالجة المركزية (CPUs).
  3. لا توجد عملية تعمل خارج المنطقة الحرجة الخاصة بها والتي تمنع العمليات الأخرى.
  4. ولا يتعين على أي عملية الانتظار إلى الأبد لدخول منطقتها الحرجة.(A ينتظر موارد B، وينتظر B موارد C، وينتظر C موارد A)

حالة السباق هي حالة غير مرغوب فيها تحدث عندما يحاول جهاز أو نظام إجراء عمليتين أو أكثر في نفس الوقت، ولكن نظرًا لطبيعة الجهاز أو النظام، يجب إجراء العمليات بالتسلسل الصحيح حتى يتم تم بشكل صحيح.

في ذاكرة الكمبيوتر أو وحدة التخزين، قد تحدث حالة سباق إذا تم تلقي أوامر قراءة وكتابة كمية كبيرة من البيانات في نفس اللحظة تقريبًا، وحاول الجهاز الكتابة فوق بعض أو كل البيانات القديمة بينما لا تزال هذه البيانات القديمة جارية يقرأ.قد تكون النتيجة واحدًا أو أكثر مما يلي:تعطل جهاز الكمبيوتر، أو "عملية غير قانونية"، أو إشعار البرنامج وإيقاف تشغيله، أو أخطاء في قراءة البيانات القديمة، أو أخطاء في كتابة البيانات الجديدة.

فيما يلي مثال كلاسيكي لرصيد الحساب البنكي والذي سيساعد المبتدئين على فهم سلاسل الرسائل في Java بسهولة.شروط السباق:

public class BankAccount {

/**
 * @param args
 */
int accountNumber;
double accountBalance;

public synchronized boolean Deposit(double amount){
    double newAccountBalance=0;
    if(amount<=0){
        return false;
    }
    else {
        newAccountBalance = accountBalance+amount;
        accountBalance=newAccountBalance;
        return true;
    }

}
public synchronized boolean Withdraw(double amount){
    double newAccountBalance=0;
    if(amount>accountBalance){
        return false;
    }
    else{
        newAccountBalance = accountBalance-amount;
        accountBalance=newAccountBalance;
        return true;
    }
}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    BankAccount b = new BankAccount();
    b.accountBalance=2000;
    System.out.println(b.Withdraw(3000));

}

جرب هذا المثال الأساسي لفهم حالة السباق بشكل أفضل:

    public class ThreadRaceCondition {

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        Account myAccount = new Account(22222222);

        // Expected deposit: 250
        for (int i = 0; i < 50; i++) {
            Transaction t = new Transaction(myAccount,
                    Transaction.TransactionType.DEPOSIT, 5.00);
            t.start();
        }

        // Expected withdrawal: 50
        for (int i = 0; i < 50; i++) {
            Transaction t = new Transaction(myAccount,
                    Transaction.TransactionType.WITHDRAW, 1.00);
            t.start();

        }

        // Temporary sleep to ensure all threads are completed. Don't use in
        // realworld :-)
        Thread.sleep(1000);
        // Expected account balance is 200
        System.out.println("Final Account Balance: "
                + myAccount.getAccountBalance());

    }

}

class Transaction extends Thread {

    public static enum TransactionType {
        DEPOSIT(1), WITHDRAW(2);

        private int value;

        private TransactionType(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    };

    private TransactionType transactionType;
    private Account account;
    private double amount;

    /*
     * If transactionType == 1, deposit else if transactionType == 2 withdraw
     */
    public Transaction(Account account, TransactionType transactionType,
            double amount) {
        this.transactionType = transactionType;
        this.account = account;
        this.amount = amount;
    }

    public void run() {
        switch (this.transactionType) {
        case DEPOSIT:
            deposit();
            printBalance();
            break;
        case WITHDRAW:
            withdraw();
            printBalance();
            break;
        default:
            System.out.println("NOT A VALID TRANSACTION");
        }
        ;
    }

    public void deposit() {
        this.account.deposit(this.amount);
    }

    public void withdraw() {
        this.account.withdraw(amount);
    }

    public void printBalance() {
        System.out.println(Thread.currentThread().getName()
                + " : TransactionType: " + this.transactionType + ", Amount: "
                + this.amount);
        System.out.println("Account Balance: "
                + this.account.getAccountBalance());
    }
}

class Account {
    private int accountNumber;
    private double accountBalance;

    public int getAccountNumber() {
        return accountNumber;
    }

    public double getAccountBalance() {
        return accountBalance;
    }

    public Account(int accountNumber) {
        this.accountNumber = accountNumber;
    }

    // If this method is not synchronized, you will see race condition on
    // Remove syncronized keyword to see race condition
    public synchronized boolean deposit(double amount) {
        if (amount < 0) {
            return false;
        } else {
            accountBalance = accountBalance + amount;
            return true;
        }
    }

    // If this method is not synchronized, you will see race condition on
    // Remove syncronized keyword to see race condition
    public synchronized boolean withdraw(double amount) {
        if (amount > accountBalance) {
            return false;
        } else {
            accountBalance = accountBalance - amount;
            return true;
        }
    }
}

لا تريد دائمًا تجاهل حالة السباق.إذا كان لديك علامة يمكن قراءتها وكتابتها بواسطة سلاسل رسائل متعددة، وتم تعيين هذه العلامة على "تم" بواسطة مؤشر ترابط واحد بحيث يتوقف مؤشر الترابط الآخر عن المعالجة عند تعيين العلامة على "تم"، فأنت لا تريد هذا "السباق شرط" ليتم القضاء عليه.في الواقع، يمكن الإشارة إلى هذه الحالة على أنها حالة عرقية حميدة.

ومع ذلك، باستخدام أداة للكشف عن حالة السباق، سيتم اكتشافها كحالة سباق ضارة.

مزيد من التفاصيل حول حالة السباق هنا، http://msdn.microsoft.com/en-us/magazine/cc546569.aspx.

فكر في عملية يجب أن تعرض العدد بمجرد زيادة العدد.أي بمجرد مكافحة الموضوع يزيد القيمة عرض الموضوع يحتاج إلى عرض القيمة المحدثة مؤخرا.

int i = 0;

انتاج |

CounterThread -> i = 1  
DisplayThread -> i = 1  
CounterThread -> i = 2  
CounterThread -> i = 3  
CounterThread -> i = 4  
DisplayThread -> i = 4

هنا مكافحة الموضوع يحصل على القفل بشكل متكرر ويقوم بتحديث القيمة من قبل عرض الموضوع يعرض ذلك.هنا توجد حالة سباق.يمكن حل حالة السباق باستخدام المزامنة

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

AtomicInteger ai = new AtomicInteger(2);
ai.getAndAdd(5);

ونتيجة لذلك، سيكون لديك 7 في الرابط "AI".على الرغم من أنك قمت بإجراءين، إلا أن كلا العمليتين تؤكدان نفس الخيط ولن يتدخل أي خيط آخر في ذلك، وهذا يعني عدم وجود شروط سباق!

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

public class Synchronized_RACECONDITION {
    private static final int NUM_INCREMENTS = 10000;

    private static int count = 0;

    public static void main(String[] args) {
        testSyncIncrement();
        testNonSyncIncrement();
    }

    private static void testSyncIncrement() {
        count = 0;

        ExecutorService executor = Executors.newFixedThreadPool(2);

        IntStream.range(0, NUM_INCREMENTS)
                .forEach(i -> executor.submit(Synchronized_RACECONDITION::incrementSync));

        ConcurrentUtils.stop(executor);

        System.out.println("   Sync: " + count);
    }

    private static void testNonSyncIncrement() {
        count = 0;

        ExecutorService executor = Executors.newFixedThreadPool(2);

        IntStream.range(0, NUM_INCREMENTS)
                .forEach(i -> executor.submit(Synchronized_RACECONDITION::increment));

        ConcurrentUtils.stop(executor);

        System.out.println("NonSync: " + count);
    }

    private static synchronized void incrementSync() {
        count = count + 1;
    }

    private static void increment() {
        count = count + 1;
    }
static  class ConcurrentUtils {

    public static void stop(ExecutorService executor) {
        try {
            executor.shutdown();
            executor.awaitTermination(60, TimeUnit.SECONDS);
        }
        catch (InterruptedException e) {
            System.err.println("termination interrupted");
        }
        finally {
            if (!executor.isTerminated()) {
                System.err.println("killing non-finished tasks");
            }
            executor.shutdownNow();
        }
    }
}
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top