ในการเขียนโปรแกรมแบบมัลติเธรดปัญหาที่สำคัญที่สุดและเกี่ยวข้องมากที่สุดควรเป็นปัญหาการซิงโครไนซ์ซึ่งเป็นจุดที่ยากและเป็นหลัก
จากการซิงโครไนซ์และผันผวนของ JDK รุ่นแรกสุดไปจนถึงอินเตอร์เฟสล็อคในแพ็คเกจ java.util.concurrent.locks ที่ให้ไว้ใน JDK 1.5 (การใช้งานรวมถึง Readlock, WriteLock และ Reentrantlock)
กลไกใดที่ใช้ในการควบคุมการซิงโครไนซ์ ปฏิกิริยาแรกคือการล็อคซึ่งควรได้รับการสัมผัสเมื่อเรียนรู้ระบบปฏิบัติการและฐานข้อมูล ในโปรแกรมมัลติเธรด Java เมื่อหลายโปรแกรมแข่งขันกับทรัพยากรเดียวกันเพื่อป้องกันการกัดกร่อนของทรัพยากรเธรดแรกที่เข้าถึงทรัพยากรจะถูกกำหนดให้ล็อควัตถุและรุ่นต่อ ๆ ไปจำเป็นต้องรอการเปิดตัวของการล็อควัตถุนี้
ใช่สิ่งที่เกี่ยวข้องมากที่สุดเกี่ยวกับการซิงโครไนซ์ของกระทู้ Java คือการใช้ทรัพยากรที่ใช้ร่วมกัน
ก่อนอื่นให้เข้าใจแหล่งข้อมูลที่ใช้ร่วมกันซึ่งมีเธรดที่มีอยู่
จาก JVM เราจำเป็นต้องประสานข้อมูลที่ใช้ร่วมกันโดยเธรด:
1. ตัวแปรอินสแตนซ์ที่บันทึกไว้ในกอง; 2. ตัวแปรคลาสบันทึกในพื้นที่วิธีการ
เมื่อเครื่องเสมือน Java โหลดคลาสแต่ละวัตถุหรือคลาสจะเชื่อมโยงกับจอภาพเพื่อป้องกันตัวแปรอินสแตนซ์ของวัตถุหรือตัวแปรคลาส แน่นอนหากวัตถุไม่มีตัวแปรอินสแตนซ์หรือคลาสไม่มีตัวแปรจอภาพจะตรวจสอบอะไร
เพื่อให้บรรลุ mutex ของจอภาพที่กล่าวถึงข้างต้นเครื่องเสมือนเชื่อมโยงล็อค (เรียกอีกอย่างว่าล็อคที่มองไม่เห็น) สำหรับแต่ละวัตถุหรือคลาส ให้ฉันอธิบายที่นี่ว่าล็อคคลาสจะถูกนำไปใช้ผ่านการล็อควัตถุเพราะเมื่อโหลดคลาส JVM จะสร้างอินสแตนซ์ของ java.lang.lass สำหรับแต่ละคลาส ดังนั้นเมื่อล็อคอยู่กับวัตถุวัตถุคลาสของคลาสนี้จะถูกล็อค
นอกจากนี้เธรดสามารถล็อควัตถุหลายครั้งซึ่งสอดคล้องกับการเผยแพร่หลายรายการ มันเป็นเครื่องคิดเลขล็อคที่จัดทำโดย JVM สำหรับแต่ละวัตถุล็อค ล็อคสุดท้ายจะถูกเพิ่ม 1 และลบ 1 และเมื่อค่าเครื่องคิดเลขเป็น 0 จะถูกปล่อยออกมา การล็อควัตถุนี้ใช้โดยจอภาพภายใน JVM และยังสร้างโดยอัตโนมัติโดย JVM โปรแกรมเมอร์ทั้งหมดไม่จำเป็นต้องเพิ่มด้วยตัวเอง
หลังจากแนะนำหลักการซิงโครไนซ์ของ Java เราจะไปที่หัวข้อและพูดคุยครั้งแรกเกี่ยวกับการใช้การซิงโครไนซ์ การซิงโครไนซ์อื่น ๆ จะถูกนำมาใช้ในบทต่อไปนี้
ลองเรียกใช้ตัวอย่างก่อน
แพ็คเกจ Thread_test; / *** ทดสอบโปรแกรมมัลติเธรดที่ขยายการใช้งานคลาสเธรด**/ คลาสสาธารณะทดสอบการอ่านเธรด {private int threadnum; Public TestThread (int threadNum) {this.threadNum = threadNum; } @Override เป็นโมฆะที่ซิงโครไนซ์สาธารณะเรียกใช้ () {สำหรับ (int i = 0; i <1000; i ++) {system.out.println ("ไม่" + threadnum + ":" + i); }} โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่นข้อยกเว้น {สำหรับ (int i = 0; i <10; i ++) {testThread ใหม่ (i) .start (); Thread.sleep (1); -
ผลการทำงาน:
No.0: 887 No.0: 888 No.0: 889 No.0: 890 No.0: 891 No.0: 892 No.0: 893 No.0: 894 No.7: 122 No.7: 123 No.7: 124
ข้างต้นเป็นเพียงคลิปอธิบายปัญหา
หากคุณระมัดระวังคุณจะพบว่าหมายเลข 0: 894 ตามด้วยหมายเลข 7: 122 ซึ่งหมายความว่ามันไม่ได้เริ่มต้นจาก 0 ถึง 999
มีการกล่าวกันว่าการซิงโครไนซ์สามารถใช้วิธีการซิงโครไนซ์หรือบล็อกซิงโครไนซ์ทำไมมันถึงทำงานที่นี่ไม่ได้?
มาวิเคราะห์กลไกการซิงโครไนซ์ก่อน การซิงโครไนซ์ทำได้ผ่านการล็อค ดังนั้นในตัวอย่างข้างต้นวัตถุใดถูกล็อคหรือคลาสใดถูกล็อค? มีสองตัวแปรภายในหนึ่งคือฉันและอีกตัวแปรหนึ่งคือ threadnum; ฉันอยู่ภายในวิธีการและ Threadnum เป็นส่วนตัว
มาเรียนรู้เกี่ยวกับกลไกการทำงานของการซิงโครไนซ์:
ในโปรแกรม Java เมื่อใช้วิธีการแบบซิงโครไนซ์หรือวิธีการซิงโครไนซ์พื้นที่นี้จะถูกทำเครื่องหมายสำหรับการตรวจสอบ ในขณะที่เมื่อ JVM จัดการโปรแกรมเมื่อโปรแกรมเข้าสู่พื้นที่การตรวจสอบมันจะล็อควัตถุหรือคลาสโดยอัตโนมัติ
ดังนั้นในตัวอย่างข้างต้นสิ่งที่ล็อคหลังจากใช้คำหลักที่ซิงโครไนซ์
เมื่อวิธีการซิงโครไนซ์ให้ล็อควัตถุอินสแตนซ์ที่เรียกวิธีการเป็นล็อควัตถุ ในตัวอย่างนี้ 10 เธรดมีวัตถุคลาสทดสอบของตัวเองดังนั้นการล็อควัตถุที่ได้มานั้นยังเป็นล็อควัตถุของตัวเองและไม่มีส่วนเกี่ยวข้องกับเธรดอื่น ๆ
ในการดำเนินการล็อควิธีการจะต้องล็อควัตถุที่ใช้ร่วมกัน
เปลี่ยนตัวอย่างข้างต้นแล้วลองดู:
แพ็คเกจ Thread_test; / *** ทดสอบโปรแกรมมัลติเธรดที่ขยายการใช้งานคลาสเธรด**/ คลาสสาธารณะทดสอบการอ่านเธรด {private int threadnum; ธงสตริงส่วนตัว; // ทำเครื่องหมาย Public TestThread (int threadnum, String Flag) {this.threadNum = threadNum; this.flag = Flag; } @Override โมฆะสาธารณะเรียกใช้ () {ซิงโครไนซ์ (แฟล็ก) {สำหรับ (int i = 0; i <1000; i ++) {system.out.println ("ไม่" + threadnum + ":" + i); }}} โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่นข้อยกเว้น {สตริงธง = สตริงใหม่ ("ธง"); สำหรับ (int i = 0; i <10; i ++) {testThread ใหม่ (i, flag) .start (); Thread.sleep (1); -
นี่คือการเพิ่มธงที่ใช้ร่วมกัน จากนั้นธงธงจะถูกซิงโครไนซ์ผ่านบล็อกที่ซิงโครไนซ์ สิ่งนี้ตรงตามเงื่อนไขสำหรับการล็อควัตถุที่ใช้ร่วมกัน
ใช่ผลลัพธ์การทำงานได้มาตามลำดับ
ผ่านบล็อกที่ซิงโครไนซ์ระบุการได้มาของการล็อควัตถุเพื่อให้ได้การซิงโครไนซ์ ดังนั้นมีวิธีอื่นใดที่สามารถนำไปใช้ผ่านวิธีการซิงโครไนซ์ได้หรือไม่?
ตามหลักการของการซิงโครไนซ์: หากสามารถรับล็อควัตถุที่ใช้ร่วมกันหรือล็อคคลาสได้การซิงโครไนซ์สามารถทำได้ ดังนั้นเราจะบรรลุเป้าหมายได้โดยการแชร์คลาสล็อคหรือไม่?
ใช่เราสามารถใช้วิธีการซิงโครไนซ์แบบคงที่ ตามลักษณะของวิธีการคงที่จะอนุญาตให้เรียกวัตถุคลาสเท่านั้นและไม่สามารถเรียกได้โดยการสร้างอินสแตนซ์วัตถุคลาส จากนั้นหากคุณได้รับการล็อคของวิธีการคงที่นี้คุณจะได้รับการล็อคคลาสและการล็อคคลาสนี้เป็นล็อคคลาสทดสอบทั้งหมดและจุดประสงค์ในการรับล็อคคลาสที่ใช้ร่วมกันจะทำได้
รหัสการใช้งานมีดังนี้:
แพ็คเกจ Thread_test; / ** * ทดสอบโปรแกรมมัลติเธรดที่ขยายการใช้งานคลาสเธรด * * @author ciding * @createTime 7 ธันวาคม 2011 9:37:25 AM * */ การทดสอบคลาสสาธารณะขยายเธรด {Private Int Threadnum; Public TestThread (int threadNum) {this.threadNum = threadNum; } public static synchronized void statictest (int threadnum) {สำหรับ (int i = 0; i <1000; i ++) {system.out.println ("ไม่" + threadnum + ":" + i); }} โมฆะคงที่สาธารณะหลัก (สตริง [] args) พ่นข้อยกเว้น {สำหรับ (int i = 0; i <10; i ++) {testThread ใหม่ (i) .start (); Thread.sleep (1); }} @Override โมฆะสาธารณะเรียกใช้ () {statictest (threadnum); - ผลการเรียกใช้จะถูกละเว้นเช่นเดียวกับในตัวอย่างที่สอง
เนื้อหาข้างต้นส่วนใหญ่อธิบายสองประเด็น: การซิงโครไนซ์บล็อกและวิธีการซิงโครไนซ์
1. บล็อกซิงโครไนซ์: ล็อควัตถุที่ได้รับคือล็อควัตถุแฟล็กในการซิงโครไนซ์ (แฟล็ก)
2. วิธีการซิงโครไนซ์: วัตถุคลาสที่เป็นของวิธีการและล็อควัตถุคลาส
วิธีการซิงโครไนซ์แบบคงที่จะถูกซิงโครไนซ์แน่นอนเนื่องจากหลายเธรดจะถูกแชร์
แทนที่จะเป็นวิธีการซิงโครไนซ์แบบคงที่พวกเขาจะถูกซิงโครไนซ์ในโหมดซิงเกิลตันเท่านั้น