هل لاحظت حالة السباق في مثال التزامن متعدد خيوط جافا؟ كيفية التعامل معها؟
نشرت: 2019-12-11 في وقت ما ، كتبت مقالًا عن مثال المستهلك المنتج وكيفية التعامل مع عملية القراءة / الكتابة بطريقة أفضل في Java. في ملاحظة مماثلة ، سنناقش في هذا البرنامج التعليمي شيئًا عن Race Condition
Thread locking
.
إذا كان لديك أي من الأسئلة التالية ، فأنت في المكان الصحيح:
- مثال على حالة سباق جافا
- مثال على جافا كائنات المزامنة
- multithreading - ما هي حالة السباق؟
- ظروف السباق والأقسام الحرجة
- ما هي حالة السباق؟
- كيفية التعامل مع حالة العرق في Java مع مثال
لماذا تحدث حالة العرق في جافا؟
تحدث حالة العرق في Java عندما يحاول two or more threads
تعديل / تحديث البيانات المشتركة في same time
.
دعنا نلقي نظرة على منطق البرنامج أدناه:
هذا مثال مصرفي بسيط للغاية حيث ستقوم
deposit
withdraw
المبالغ100 times
. ستقوم بإيداع 100 دولار إجمالاً 100 مرة = 100 دولار × 100 = 10000 دولار وستسحب 50 دولارًا إجماليًا 100 مرة = 50 دولارًا × 100 = 5000 دولار. في نهاية البرنامج ، يجب أن يكون لديك 5000 دولار في البنك الذي تتعامل معه.
فيما يلي الخطوات:
- قم بإنشاء فئة CrunchifyRaceCondition.java
- قم بإنشاء فئة CrunchifyTransaction.java
- قم بإنشاء فئة CrunchifyBankAccount.java
- سنقوم بتشغيل class CrunchifyRaceCondition وسيبدأ إيداع وسحب الحلقة 100 مرة.
- سنقوم بتشغيله
with Synchronized block
للتحقق من النتيجة - سنقوم بتشغيله
without Synchronized block
للتحقق من النتيجة
CrunchifyRaceCondition.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package com . crunchify . tutorial ; /** * @author Crunchify.com * */ public class CrunchifyRaceCondition { public static void main ( String [ ] args ) { CrunchifyBankAccount crunchifyAccount = new CrunchifyBankAccount ( "CrunchifyAccountNumber" ) ; // Total Expected Deposit: 10000 (100 x 100) for ( int i = 0 ; i < 100 ; i ++ ) { CrunchifyTransaction t = new CrunchifyTransaction ( crunchifyAccount , CrunchifyTransaction . TransactionType . DEPOSIT_MONEY , 100 ) ; t . start ( ) ; } // Total Expected Withdrawal: 5000 (100 x 50) for ( int i = 0 ; i < 100 ; i ++ ) { CrunchifyTransaction t = new CrunchifyTransaction ( crunchifyAccount , CrunchifyTransaction . TransactionType . WITHDRAW_MONEY , 50 ) ; t . start ( ) ; } // Let's just wait for a second to make sure all thread execution completes. try { Thread . sleep ( 1000 ) ; } catch ( InterruptedException e ) { System . out . println ( e ) ; } // Expected account balance is 5000 System . out . println ( "Final Account Balance: " + crunchifyAccount . getAccountBalance ( ) ) ; } } |
CrunchifyTransaction.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
package com . crunchify . tutorial ; /** * @author Crunchify.com */ class CrunchifyTransaction extends Thread { public static enum TransactionType { DEPOSIT_MONEY ( 1 ) , WITHDRAW_MONEY ( 2 ) ; private TransactionType ( int value ) { } } ; private TransactionType transactionType ; private CrunchifyBankAccount crunchifyAccount ; private double crunchifyAmount ; /* * If transactionType == 1, depositAmount() else if transactionType == 2 withdrawAmount() */ public CrunchifyTransaction ( CrunchifyBankAccount crunchifyAccount , TransactionType transactionType , double crunchifyAmount ) { this . transactionType = transactionType ; this . crunchifyAccount = crunchifyAccount ; this . crunchifyAmount = crunchifyAmount ; } public void run ( ) { switch ( this . transactionType ) { case DEPOSIT_MONEY : depositAmount ( ) ; printBalance ( ) ; break ; case WITHDRAW_MONEY : withdrawAmount ( ) ; printBalance ( ) ; break ; default : System . out . println ( "NOT A VALID TRANSACTION" ) ; } } public void depositAmount ( ) { this . crunchifyAccount . depositAmount ( this . crunchifyAmount ) ; } public void withdrawAmount ( ) { this . crunchifyAccount . withdrawAmount ( crunchifyAmount ) ; } public void printBalance ( ) { System . out . println ( Thread . currentThread ( ) . getName ( ) + " : TransactionType: " + this . transactionType + ", Amount: " + this . crunchifyAmount ) ; System . out . println ( "New Account Balance: " + this . crunchifyAccount . getAccountBalance ( ) ) ; } } |

CrunchifyBankAccount.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package com . crunchify . tutorial ; /** * @author Crunchify.com */ class CrunchifyBankAccount { private String crunchifyAccountNumber ; private double crunchifyAccountBalance ; public String getAccountNumber ( ) { return crunchifyAccountNumber ; } public double getAccountBalance ( ) { return crunchifyAccountBalance ; } public CrunchifyBankAccount ( String crunchifyAccountNumber ) { this . crunchifyAccountNumber = crunchifyAccountNumber ; } // Make a note of this line -- synchronized keyword added public synchronized boolean depositAmount ( double amount ) { if ( amount < 0 ) { return false ; } else { crunchifyAccountBalance = crunchifyAccountBalance + amount ; return true ; } } // Make a note of this line -- synchronized keyword added public synchronized boolean withdrawAmount ( double amount ) { if ( amount > crunchifyAccountBalance ) { return false ; } else { crunchifyAccountBalance = crunchifyAccountBalance - amount ; return true ; } } } |
يرجى مراجعة السطر 24 و 34 أعلاه. احتفظ بهذه الكلمة الأساسية Synchronized
وقم بتشغيل برنامجك. يجب أن ترى النتيجة الصحيحة كما تراها في الصورة أدناه.
الآن قم بإزالة الكلمات الأساسية المتزامنة من السطر 24 و 34 وقم بتشغيل نفس البرنامج.
قد تحتاج إلى تشغيل هذا البرنامج عدة مرات لمعرفة المشكلة. في جافا ليس هناك ما يضمن أنك سترى حالة السباق في جميع الأوقات.
إذا كان لديك تطبيق على مستوى المؤسسة وتتحدث عن ملايين المعاملات في الثانية ، فقد تتسبب حالة السباق في كارثة لشركتك.
السؤال الآن هو كيف تتجنب حالة العرق في تطبيق Java الخاص بك؟
- إذا كانت حالة السباق قيد التحديثات على بعض هياكل البيانات المشتركة في الذاكرة ، فأنت بحاجة إلى مزامنة الوصول والتحديثات إلى بنية البيانات بالطريقة المناسبة.
- إذا كانت حالة السباق في تحديثات لقاعدة البيانات الخاصة بك ، فأنت بحاجة إلى إعادة هيكلة SQL الخاص بك لاستخدام المعاملات على المستوى المناسب من التفاصيل.
- ليس من الجيد إجراء اختبار التحميل قبل بدء الإنتاج. قد يتسبب المزيد من الحمل في حدوث حالة سباق نادرة. من الأفضل إصلاحه قبل إصلاحه لاحقًا.
- تأكد من عدم وجود متغير عام تكتب إليه.
- في Java ، يحتوي كل كائن على شاشة واحدة ومفتاح كائن واحد فقط مرتبط به. تحتوي الشاشة الفردية على عدة أبواب بداخلها ، ومع ذلك ، يُشار إلى كل منها بالكلمة الرئيسية
synchronized
. عندما يمر خيط فوق الكلمة الأساسيةsynchronized
، فإنه يقفل جميع الأبواب بشكل فعال. - بالطبع ، إذا لم يمر الخيط عبر الكلمة الأساسية
synchronized
، فإنه لم يغلق الباب ، وبعض الخيوط الأخرى عبارة عن مداخلة مجانية في أي وقت.