ใน Java การซิงโครไนซ์คำหลักสามารถใช้สำหรับการควบคุมการซิงโครไนซ์เธรดเพื่อให้บรรลุการเข้าถึงทรัพยากรคีย์ตามลำดับและหลีกเลี่ยงความไม่สอดคล้องกันของข้อมูลที่เกิดจากการดำเนินการพร้อมกันหลายเธรด หลักการของการซิงโครไนซ์คือการตรวจสอบวัตถุ (ล็อค) เฉพาะเธรดที่ได้มาซึ่งจอภาพสามารถดำเนินการต่อไปได้มิฉะนั้นเธรดจะรอที่จะได้รับจอภาพ แต่ละวัตถุหรือคลาสใน Java มีล็อคที่เกี่ยวข้อง สำหรับวัตถุมันจะตรวจสอบตัวแปรอินสแตนซ์ของวัตถุนี้ สำหรับชั้นเรียนมันจะตรวจสอบตัวแปรคลาส (คลาสเองเป็นวัตถุของคลาสคลาสดังนั้นการล็อคที่เกี่ยวข้องกับคลาสก็เป็นล็อควัตถุ) มีสองวิธีในการใช้คำหลักที่ซิงโครไนซ์: วิธีการซิงโครไนซ์และบล็อกซิงโครไนซ์ พื้นที่ตรวจสอบทั้งสองเกี่ยวข้องกับวัตถุที่แนะนำ เมื่อถึงพื้นที่การตรวจสอบนี้ JVM จะล็อควัตถุอ้างอิงและเมื่อออกจากการล็อคบนวัตถุอ้างอิงจะถูกปล่อยออกมา (JVM จะปล่อยล็อคเมื่อมีข้อยกเว้น) ล็อควัตถุเป็นกลไกภายในของ JVM คุณจะต้องเขียนวิธีการซิงโครไนซ์หรือบล็อกซิงโครไนซ์เท่านั้น เมื่อใช้งานพื้นที่ตรวจสอบ JVM จะได้รับหรือปล่อยล็อคโดยอัตโนมัติ
ตัวอย่างที่ 1
ก่อนอื่นให้ดูตัวอย่างแรก ใน Java มีพื้นที่สำคัญเพียงแห่งเดียวของวัตถุเดียวกันที่ได้รับอนุญาตให้เข้าถึงได้ในเวลาเดียวกัน
แพ็คเกจพร้อมกัน; คลาสสาธารณะ main8 {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {บัญชีบัญชี = บัญชีใหม่ (); Account.setBalance (1,000); บริษัท บริษัท = บริษัท ใหม่ (บัญชี); Thread CompanyThread = เธรดใหม่ (บริษัท ); ธนาคารธนาคาร = ธนาคารใหม่ (บัญชี); Thread BankThread = ใหม่เธรด (ธนาคาร); System.out.printf ("บัญชี: ยอดคงเหลือเริ่มต้น: %f/n", account.getBalance ()); companythread.start (); bankthread.start (); ลอง {// เข้าร่วม () เมธอดรอให้สองเธรดนี้เสร็จสิ้น companythread.join (); bankthread.join (); System.out.printf ("บัญชี: ยอดคงเหลือสุดท้าย: %f/n", account.getBalance ()); } catch (interruptedException e) {e.printStackTrace (); - /*บัญชี*/บัญชีคลาส {ยอดคงเหลือคู่ส่วนตัว; /*เพิ่มข้อมูลขาเข้าเพื่อสมดุล*/ โมฆะที่ซิงโครไนซ์สาธารณะ addamount (จำนวนสองเท่า) {double tmp = ยอดคงเหลือ; ลอง {thread.sleep (10); } catch (interruptedException e) {e.printStackTrace (); } tmp += จำนวน; ยอดคงเหลือ = tmp; } /*หักข้อมูลขาเข้าจากยอดคงเหลือ* / โมฆะสาธารณะที่ซิงโครไนซ์ subtractAmount (จำนวนสองเท่า) {double tmp = ยอดคงเหลือ; ลอง {thread.sleep (10); } catch (interruptedException e) {e.printStackTrace (); } tmp -= จำนวน; ยอดคงเหลือ = tmp; } public double getBalance () {Balance return; } โมฆะสาธารณะ setBalance (สมดุลสองเท่า) {this.balance = สมดุล; - /*Bank*/Class Bank ใช้งาน Runnable {บัญชีส่วนตัว; ธนาคารสาธารณะ (บัญชีบัญชี) {this.account = บัญชี; } @Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (int i = 0; i <100; i ++) {account.subtractAmount (1,000); - /*บริษัท*/คลาส บริษัท ใช้งาน Runnable {บัญชีส่วนตัว; บริษัท มหาชน (บัญชีบัญชี) {this.account = บัญชี; } @Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (int i = 0; i <100; i ++) {account.addamount (1,000); -คุณได้พัฒนาแอปพลิเคชันจำลองสำหรับบัญชีธนาคารที่สามารถเติมเงินและหักยอดคงเหลือได้ โปรแกรมนี้ชาร์จบัญชีโดยเรียกวิธี Addamount () 100 ครั้งฝาก 1,000 ครั้งในแต่ละครั้ง จากนั้นหักยอดคงเหลือในบัญชีโดยเรียกใช้วิธี SubtractAmount () 100 ครั้งหัก 1,000 ครั้งในแต่ละครั้ง เราคาดว่ายอดคงเหลือสุดท้ายของบัญชีจะเท่ากับยอดคงเหลือเริ่มต้นและเรานำไปใช้ผ่านคำหลักที่ซิงโครไนซ์
หากคุณต้องการดูปัญหาการเข้าถึงที่เกิดขึ้นพร้อมกันของข้อมูลที่ใช้ร่วมกันคุณจะต้องลบคำหลักที่ซิงโครไนซ์ใน AddAmount () และ SubtractAmount () คำหลัก หากไม่มีคำหลักที่ซิงโครไนซ์ค่าสมดุลที่พิมพ์จะไม่สอดคล้องกัน หากคุณเรียกใช้โปรแกรมนี้หลายครั้งคุณจะได้ผลลัพธ์ที่แตกต่างกัน เนื่องจาก JVM ไม่รับประกันลำดับการดำเนินการของเธรดในแต่ละครั้งที่ทำงานเธรดจะอ่านและแก้ไขยอดคงเหลือบัญชีในคำสั่งซื้อที่แตกต่างกันทำให้เกิดผลลัพธ์สุดท้ายที่แตกต่างกัน
วิธีการของวัตถุถูกประกาศโดยใช้คำหลักที่ซิงโครไนซ์และสามารถเข้าถึงได้โดยเธรดเดียวเท่านั้น หากเธรด A กำลังดำเนินการวิธีการซิงโครไนซ์ SyncMethoda (), เธรด B ต้องการดำเนินการวิธีการซิงโครไนซ์อื่น ๆ SyncMethodb () ของวัตถุนี้เธรด B จะถูกบล็อกจนกว่าเธรด A จะเข้าถึงเสร็จสมบูรณ์ แต่ถ้าเธรด B เข้าถึงวัตถุที่แตกต่างกันของคลาสเดียวกันจะไม่ถูกบล็อกเธรด
ตัวอย่างที่ 2
แสดงให้เห็นถึงปัญหาที่วิธีการซิงโครไนซ์แบบคงที่และวิธีการซิงโครไนซ์แบบไม่คงที่ในวัตถุเดียวกันสามารถเข้าถึงได้โดยหลายเธรดในเวลาเดียวกัน ตรวจสอบ
แพ็คเกจพร้อมกัน; คลาสสาธารณะ main8 {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {บัญชีบัญชี = บัญชีใหม่ (); Account.setBalance (1,000); บริษัท บริษัท = บริษัท ใหม่ (บัญชี); Thread CompanyThread = เธรดใหม่ (บริษัท ); ธนาคารธนาคาร = ธนาคารใหม่ (บัญชี); Thread BankThread = ใหม่เธรด (ธนาคาร); System.out.printf ("บัญชี: ยอดคงเหลือเริ่มต้น: %f/n", account.getBalance ()); companythread.start (); bankthread.start (); ลอง {// เข้าร่วม () เมธอดรอให้สองเธรดนี้เสร็จสิ้น companythread.join (); bankthread.join (); System.out.printf ("บัญชี: ยอดคงเหลือสุดท้าย: %f/n", account.getBalance ()); } catch (interruptedException e) {e.printStackTrace (); - /*บัญชี*/บัญชีคลาส {/*เปลี่ยนเป็นตัวแปรสแตติกที่นี่*/ยอดคงเหลือคู่คงที่คู่ = 0; /*เพิ่มข้อมูลที่เข้ามาในยอดคงเหลือโปรดทราบว่ามีการแก้ไขด้วยโมฆะแบบคงที่*/ สาธารณะคงที่แบบคงที่ synchronized addamount (จำนวนสองเท่า) {double tmp = ยอดคงเหลือ; ลอง {thread.sleep (10); } catch (interruptedException e) {e.printStackTrace (); } tmp += จำนวน; ยอดคงเหลือ = tmp; } /*หักข้อมูลขาเข้าจากยอดคงเหลือ* / โมฆะสาธารณะที่ซิงโครไนซ์ SubtractAmount (จำนวนสองเท่า) {double tmp = ยอดคงเหลือ; ลอง {thread.sleep (10); } catch (interruptedException e) {e.printStackTrace (); } tmp -= จำนวน; ยอดคงเหลือ = tmp; } public double getBalance () {Balance return; } โมฆะสาธารณะ setBalance (สมดุลสองเท่า) {this.balance = สมดุล; - /*Bank*/Class Bank ใช้งาน Runnable {บัญชีส่วนตัว; ธนาคารสาธารณะ (บัญชีบัญชี) {this.account = บัญชี; } @Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (int i = 0; i <100; i ++) {account.subtractAmount (1,000); - /*บริษัท*/คลาส บริษัท ใช้งาน Runnable {บัญชีส่วนตัว; บริษัท มหาชน (บัญชีบัญชี) {this.account = บัญชี; } @Override โมฆะสาธารณะเรียกใช้ () {สำหรับ (int i = 0; i <100; i ++) {account.addamount (1,000); -ฉันเพิ่งเพิ่มคำหลักคงที่เพื่อแก้ไขยอดคงเหลือในตัวอย่างก่อนหน้าและเมธอด addamount () ยังสามารถแก้ไขคำหลักคงที่ คุณสามารถทดสอบผลการดำเนินการด้วยตนเองและการดำเนินการแต่ละครั้งจะมีผลลัพธ์ที่แตกต่างกัน!
สรุปบางส่วน: