คุณสังเกตเห็นสภาพการแข่งขันในตัวอย่าง Java Multi-threading Concurrency หรือไม่? จะจัดการกับมันอย่างไร?
เผยแพร่แล้ว: 2019-12-11 เมื่อก่อนฉันได้เขียนบทความเกี่ยวกับตัวอย่างผู้บริโภคของผู้ผลิตและวิธีจัดการการอ่าน/เขียนวิธีที่ดีกว่าใน Java ในบันทึกที่คล้ายกัน ในบทช่วยสอนนี้ เราจะพูดถึงบางอย่างเกี่ยวกับ Race Condition
และ Thread locking
หากคุณมีคำถามใด ๆ ด้านล่างแสดงว่าคุณมาถูกที่แล้ว:
- ตัวอย่างสภาพการแข่งขัน Java
- ตัวอย่าง mutex java
- มัลติเธรด – สภาวะการแข่งขันคืออะไร?
- เงื่อนไขการแข่งขันและส่วนสำคัญ
- สภาพการแข่งขันคืออะไร?
- วิธีจัดการกับ Race Condition ใน Java ด้วย Example
ทำไมสภาพการแข่งขันใน Java เกิดขึ้น?
สภาพการแข่งขันใน Java เกิดขึ้นเมื่อ two or more threads
ไปพยายามแก้ไข/อัปเดตข้อมูลที่แชร์พร้อม same time
ลองดูที่ด้านล่าง ตรรกะของโปรแกรม:
นี่เป็นตัวอย่างการธนาคารที่ง่ายมากที่คุณจะ
deposit
และwithdraw
จำนวน100 times
คุณจะฝากเงิน $100 ทั้งหมด 100 ครั้ง = $100 x 100 = $10,000 และคุณจะถอน $50 ทั้งหมด 100 ครั้ง = $50 x 100 = $5,000 เมื่อสิ้นสุดโปรแกรม คุณควรมีเงิน $5,000 ในธนาคารของคุณ
นี่คือขั้นตอน:
- สร้างคลาส CrunchifyRaceCondition.java
- สร้างคลาส CrunchifyTransaction.java
- สร้างคลาส CrunchifyBankAccount.java
- เราจะเรียกใช้คลาส 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 ไม่มีการรับประกันว่าคุณจะเห็น Race condition ตลอดเวลา
หากคุณมีแอปพลิเคชันระดับองค์กรและคุณกำลังพูดถึงธุรกรรมหลายล้านรายการต่อวินาที สภาวะการแข่งขันอาจทำให้เกิดหายนะสำหรับบริษัทของคุณ
ตอนนี้คำถามคือจะหลีกเลี่ยง Race Condition ในแอปพลิเคชัน Java ของคุณได้อย่างไร
- ถ้าสภาวะการแย่งชิงอยู่ในการอัพเดตโครงสร้างข้อมูลในหน่วยความจำที่ใช้ร่วมกันบางตัว คุณต้องซิงโครไนซ์การเข้าถึงและอัพเดตโครงสร้างข้อมูลด้วยวิธีที่เหมาะสม
- หากสภาวะการแย่งชิงอยู่ในการอัปเดตฐานข้อมูลของคุณ คุณต้องปรับโครงสร้าง SQL ของคุณใหม่เพื่อใช้ธุรกรรมในระดับความละเอียดที่เหมาะสม
- ไม่ควรทำการทดสอบโหลดก่อนใช้งานจริงในเวอร์ชันที่ใช้งานจริง โหลดมากขึ้นอาจทำให้สภาพการแข่งขันหายาก แก้ก่อนดีกว่าค่อยมาแก้ทีหลัง
- ตรวจสอบให้แน่ใจว่าคุณไม่มีตัวแปรส่วนกลางที่คุณเขียนถึง
- ใน Java ทุกอ็อบเจ็กต์มีเพียงหนึ่งมอนิเตอร์และ mutex ที่เชื่อมโยงอยู่ จอภาพเดียวมีประตูหลายบาน อย่างไรก็ตาม แต่ละจอระบุด้วยคีย์เวิร์ดที่
synchronized
เมื่อเธรดผ่านคีย์เวิร์ดที่synchronized
มันจะล็อกประตูทั้งหมดอย่างมีประสิทธิภาพ - แน่นอน ถ้าเธรดไม่ผ่านระหว่างคีย์เวิร์ดที่
synchronized
เธรดนั้นก็ไม่ได้ล็อคประตู และเธรดอื่นๆ บางส่วนก็อาจเข้ามาฟรีได้ทุกเมื่อ