Java Multi-threading Concurrency Örneğinde Yarış Koşullarını fark ettiniz mi? Nasıl başa çıkılır bununla?
Yayınlanan: 2019-12-11 Bir süre önce, Üretici Tüketici Örneği ve Java'da okuma/yazma işleminin daha iyi nasıl ele alınacağı hakkında bir makale yazdım. Benzer bir notta, bu eğitimde Race Condition
ve Thread locking
hakkında bir şeyler tartışacağız.
Aşağıdaki sorulardan herhangi birine sahipseniz doğru yerdesiniz:
- Java yarış durumu örneği
- muteks java örneği
- multithreading – Yarış durumu nedir?
- Yarış Koşulları ve Kritik Bölümler
- Yarış durumu nedir?
- Örnek ile Java'da Yarış Durumu ile nasıl başa çıkılır
Java'da Yarış Durumu Neden Oluşur?
Java'daki yarış durumu, two or more threads
same time
paylaşılan verileri değiştirmeye/güncellemeye çalıştığında oluşur.
Aşağıdaki Program mantığına bir göz atalım:
Bu,
100 times
paradeposit
withdraw
çok basit bir bankacılık örneğidir. Toplam 100$'ı 100 defa yatıracaksınız = 100$ x 100 = 10.000$ ve toplam 50$'ı 100 defa çekeceksiniz = 50$ x 100 = 5.000$. Programın tamamlanmasının sonunda bankanızda 5000$ olmalıdır.
İşte adımlar:
- CrunchifyRaceCondition.java sınıfını oluşturun
- CrunchifyTransaction.java sınıfını oluşturun
- CrunchifyBankAccount.java sınıfını oluşturun
- CrunchifyRaceCondition sınıfını çalıştıracağız ve 100 kez para yatırma ve çekme döngüsüne başlayacak.
- Sonucu kontrol etmek için
with Synchronized block
çalıştıracağız - Sonucu kontrol etmek için
without Synchronized block
çalıştıracağız
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 ; } } } |
Lütfen yukarıdaki 24. ve 34. satırları kontrol edin. Synchronized
anahtar kelimeyi saklayın ve programınızı çalıştırın. Aşağıdaki resimde gördüğünüz gibi doğru sonucu görmelisiniz.
Şimdi 24. ve 34. satırdan senkronize edilmiş anahtar kelimeyi kaldırın ve aynı programı çalıştırın.
Bir sorunu görmek için bu programı birden çok kez çalıştırmanız gerekebilir. Java'da her zaman Yarış durumunu göreceğinizin garantisi yoktur.
Kurumsal düzeyde bir uygulamanız varsa ve saniyede milyonlarca işlemden bahsediyorsanız, yarış durumu şirketiniz için felakete neden olabilir.
Şimdi soru şu: Java Uygulamanızda Yarış Durumundan nasıl kaçınılır?
- Yarış durumu, bazı paylaşılan bellek içi veri yapılarında güncellemelerdeyse, veri yapısına erişimi ve güncellemeleri uygun şekilde senkronize etmeniz gerekir.
- Yarış koşulu, veritabanınızdaki güncellemelerdeyse, işlemleri uygun ayrıntı düzeyinde kullanmak için SQL'inizi yeniden yapılandırmanız gerekir.
- Üretimde canlı yayına geçmeden önce Yük testi yapmak kötü bir fikir değildir. Daha fazla yük, nadir Yarış Durumuna neden olabilir. Daha sonra düzeltmektense önce düzeltmek daha iyidir.
- Yazdığınız global bir değişkeniniz olmadığından emin olun.
- Java'da, her nesnenin kendisiyle ilişkilendirilmiş bir ve yalnızca bir monitörü ve mutex'i vardır. Tek monitörün içinde birkaç kapı vardır, ancak her biri
synchronized
anahtar kelimeyle belirtilir. Bir iş parçacığısynchronized
anahtar sözcüğünü geçtiğinde, tüm kapıları etkin bir şekilde kilitler. - Elbette, bir iş parçacığı
synchronized
anahtar sözcüğünü geçmezse, kapıyı kilitlememiştir ve başka bir iş parçacığı herhangi bir zamanda ücretsiz olarak girebilmektedir.