ฉันจำได้ว่าเมื่อฉันเริ่มเรียนรู้ Java ครั้งแรกพบว่ามีการซิงโครไนซ์เมื่อฉันพบสถานการณ์มัลติเธรด เมื่อเทียบกับเราในเวลานั้นการซิงโครไนซ์นั้นมีมนต์ขลังและทรงพลัง ในเวลานั้นเราตั้งชื่อ "การซิงโครไนซ์" ซึ่งก็กลายเป็นยาที่ดีสำหรับเราในการแก้สถานการณ์หลายเธรด แต่เมื่อเราเรียนรู้เรารู้ว่าการซิงโครไนซ์เป็นล็อคเฮฟวี่เวทและเมื่อเทียบกับการล็อคมันจะดูใหญ่มากจนเราคิดว่ามันไม่ได้มีประสิทธิภาพและค่อยๆละทิ้งมัน
เป็นที่ยอมรับด้วยการเพิ่มประสิทธิภาพที่หลากหลายของ Javas SE 1.6 ในการซิงโครไนซ์ซิงโครไนซ์จะไม่ปรากฏว่าหนัก ด้านล่างให้ติดตาม LZ เพื่อสำรวจกลไกการใช้งานของการซิงโครไนซ์วิธีการที่ Java ปรับให้เหมาะสมกลไกการปรับให้เหมาะสมที่สุดกลไกการล็อคโครงสร้างการจัดเก็บและกระบวนการอัพเกรด
หลักการดำเนินการ
การซิงโครไนซ์สามารถมั่นใจได้ว่าวิธีการหรือบล็อกรหัสกำลังทำงานอยู่และมีเพียงวิธีเดียวเท่านั้นที่สามารถเข้าสู่พื้นที่วิกฤตได้ในเวลาเดียวกัน ในขณะเดียวกันก็สามารถตรวจสอบให้แน่ใจว่าการมองเห็นหน่วยความจำของตัวแปรที่ใช้ร่วมกัน
วัตถุทุกชิ้นใน Java สามารถใช้เป็นล็อคซึ่งเป็นพื้นฐานสำหรับการซิงโครไนซ์เพื่อใช้การซิงโครไนซ์:
วิธีการซิงโครไนซ์ทั่วไปการล็อคเป็นวิธีการซิงโครไนซ์แบบคงที่ของวัตถุอินสแตนซ์ปัจจุบันล็อคคือบล็อกวิธีการซิงโครไนซ์วัตถุคลาสปัจจุบันล็อคเป็นวัตถุในวงเล็บเมื่อเธรดเข้าถึงบล็อกรหัสการซิงโครไนซ์ก่อนอื่นต้องให้ล็อคเพื่อเรียกใช้รหัสการซิงโครไนซ์ เมื่อมีข้อยกเว้นออกหรือถูกโยนล็อคจะต้องถูกปล่อยออกมา ดังนั้นมันจะใช้กลไกนี้อย่างไร? ก่อนอื่นดูรหัสง่าย ๆ :
คลาสสาธารณะ SynchronizedTest {public synchronized void test1 () {} public void test2 () {ซิงโครไนซ์ (นี่) {}}}}ใช้เครื่องมือ Javap เพื่อดูข้อมูลไฟล์คลาสที่สร้างขึ้นเพื่อวิเคราะห์การใช้งานการซิงโครไนซ์
ดังที่เห็นได้จากด้านบนบล็อกรหัสการซิงโครไนซ์จะถูกนำมาใช้โดยใช้คำแนะนำ MonitorEnenter และ Monitorexit วิธีการซิงโครไนซ์ (ไม่แสดงให้เห็นว่าคุณต้องดูการใช้งาน JVM พื้นฐาน) ขึ้นอยู่กับการใช้งาน ACC_SYNCHRONIZED บน Method Modifier
บล็อกรหัสแบบซิงโครนัส: คำสั่ง MonitorEnter ถูกแทรกลงในตำแหน่งเริ่มต้นของบล็อกรหัสการซิงโครไนซ์และคำสั่ง Monitorexit จะถูกแทรกลงในตำแหน่งสิ้นสุดของบล็อกรหัสซิงโครไนซ์ JVM จำเป็นต้องตรวจสอบให้แน่ใจว่า MonitorEnter แต่ละตัวมี monitorexit ที่สอดคล้องกับมัน วัตถุใด ๆ มีจอภาพที่เกี่ยวข้องและเมื่อมีการตรวจสอบจะถูกล็อค เมื่อเธรดดำเนินการคำสั่ง MonitorEnter มันจะพยายามที่จะได้รับความเป็นเจ้าของจอภาพที่สอดคล้องกับวัตถุนั่นคือพยายามที่จะรับล็อคของวัตถุ
วิธีการซิงโครไนซ์: วิธีการซิงโครไนซ์จะถูกแปลเป็นวิธีการเรียกใช้วิธีธรรมดาและคำแนะนำในการส่งคืนเช่น: คำแนะนำ invokevirtual และ areturn ไม่มีคำแนะนำพิเศษที่ระดับ VM bytecode เพื่อใช้วิธีการแก้ไขแบบซิงโครไนซ์ แต่ตำแหน่งแฟล็กที่ซิงโครไนซ์ 1 ในฟิลด์ Method Access_flags ของวิธีการถูกวางไว้ในตารางเมธอดของไฟล์คลาสแสดงว่าวิธีการนี้เป็นวิธีที่ซิงโครไนซ์และใช้วัตถุที่เรียกวิธีการหรือคลาสที่เป็นของวิธีการที่จะแสดง Klass เป็นวัตถุล็อค (อ้างอิง: http://ww.vevb.com/article
ลองวิเคราะห์ต่อไป แต่ก่อนที่เราจะลึกลงไปเราต้องเข้าใจแนวคิดที่สำคัญสองประการ: หัววัตถุ Java และการตรวจสอบ
ส่วนหัวของวัตถุ Java, Monitor
ส่วนหัวและจอมอนิเตอร์ของ Java เป็นพื้นฐานสำหรับการใช้งานแบบซิงโครไนซ์! ต่อไปนี้เป็นการแนะนำรายละเอียดเกี่ยวกับแนวคิดทั้งสองนี้
หัววัตถุ Java
ล็อคที่ใช้สำหรับซิงโครไนซ์ตั้งอยู่ในส่วนหัวของวัตถุ Java ดังนั้นส่วนหัวของวัตถุ Java คืออะไร? ส่วนหัววัตถุของเครื่องเสมือนฮอตสปอตส่วนใหญ่มีข้อมูลสองส่วน: เครื่องหมายคำ (มาร์คฟิลด์) และตัวชี้ KLASS (ประเภทตัวชี้) Klass Point เป็นตัวชี้ไปยังข้อมูลเมตาของคลาสของวัตถุ เครื่องเสมือนใช้ตัวชี้นี้เพื่อพิจารณาว่าอินสแตนซ์ของคลาสใดที่วัตถุคือ Mark Word ใช้เพื่อจัดเก็บข้อมูลรันไทม์ของวัตถุ มันเป็นกุญแจสำคัญในการใช้ล็อคน้ำหนักเบาและล็อคลำเอียงดังนั้นสิ่งต่อไปนี้จะมุ่งเน้นไปที่มัน
ทำเครื่องหมายคำ
MARK Word ใช้ในการจัดเก็บข้อมูลรันไทม์ของวัตถุเองเช่นรหัสแฮช (แฮชโฟตอน) อายุการสร้าง GC, สถานะล็อคสถานะการล็อคที่จัดขึ้นโดยเธรด, ID เธรดลำเอียง, การประทับเวลาแบบลำเอียง ฯลฯ โดยทั่วไปจะใช้รหัสเครื่องจักรสองเครื่อง (ใน 32 บิต เครื่องเสมือน JVM สามารถกำหนดขนาดของวัตถุ Java ผ่านข้อมูลข้อมูลเมตาของวัตถุ Java แต่ไม่สามารถยืนยันขนาดของอาร์เรย์จากข้อมูลเมตาของอาร์เรย์ได้ดังนั้นชิ้นส่วนจะใช้เพื่อบันทึกความยาวของอาร์เรย์ รูปต่อไปนี้คือโครงสร้างการจัดเก็บของส่วนหัววัตถุ Java (เครื่องเสมือน 32 บิต):
ข้อมูลส่วนหัวของวัตถุเป็นค่าจัดเก็บเพิ่มเติมที่เป็นอิสระจากข้อมูลที่กำหนดโดยวัตถุเอง อย่างไรก็ตามเมื่อพิจารณาถึงประสิทธิภาพเชิงพื้นที่ของเครื่องเสมือนจริง Mark Word ได้รับการออกแบบให้เป็นโครงสร้างข้อมูลที่ไม่ได้กำหนดเพื่อจัดเก็บข้อมูลให้มากที่สุดเท่าที่จะเป็นไปได้ในหน่วยความจำอวกาศขนาดเล็กมาก มันจะนำพื้นที่เก็บข้อมูลของตัวเองกลับมาใช้ใหม่ตามสถานะของวัตถุ กล่าวคือ Mark Word จะเปลี่ยนไปตามการทำงานของโปรแกรมและสถานะการเปลี่ยนแปลงมีดังนี้ (เครื่องเสมือน 32 บิต):
การแนะนำสั้น ๆ เกี่ยวกับส่วนหัวของวัตถุ Java มาดูที่จอภาพ
เฝ้าสังเกต
จอภาพคืออะไร? เราสามารถเข้าใจได้ว่าเป็นเครื่องมือการซิงโครไนซ์หรือกลไกการซิงโครไนซ์ซึ่งมักจะอธิบายว่าเป็นวัตถุ
เช่นเดียวกับทุกสิ่งเป็นวัตถุวัตถุ Java ทั้งหมดเป็นจอภาพธรรมชาติและวัตถุ Java แต่ละชิ้นมีศักยภาพที่จะกลายเป็นจอภาพเพราะในการออกแบบ Java วัตถุ Java แต่ละชิ้นมีล็อคที่มองไม่เห็นเนื่องจากมันออกมาจากมดลูก มันเรียกว่าล็อคภายในหรือล็อคจอภาพ
การตรวจสอบเป็นโครงสร้างข้อมูลที่เป็นของเอกชนโดยเธรด แต่ละเธรดมีรายการบันทึกการตรวจสอบที่มีอยู่และรายการที่มีอยู่ทั่วโลก วัตถุที่ล็อคแต่ละชิ้นจะเชื่อมโยงกับจอภาพ (ล็อกคำในเครื่องหมายมาร์กเวิร์ดของจุดส่วนหัวของวัตถุไปยังที่อยู่เริ่มต้นของจอภาพ) ในเวลาเดียวกันมีฟิลด์เจ้าของในจอภาพที่เก็บตัวระบุที่ไม่ซ้ำกันของเธรดที่เป็นเจ้าของล็อคซึ่งบ่งชี้ว่าล็อคถูกครอบครองโดยเธรด โครงสร้างของมันมีดังนี้:
เจ้าของ: null ในตอนแรกหมายความว่าไม่มีเธรดในปัจจุบันเป็นเจ้าของ monitorrecord เมื่อเธรดเป็นเจ้าของล็อคได้สำเร็จจะบันทึกตัวตนที่ไม่ซ้ำกันของเธรดและเมื่อล็อคถูกปล่อยออกมามันจะถูกตั้งค่าเป็นโมฆะ
entryQ: เชื่อมโยงระบบ mutex (semaphore) เพื่อบล็อกเธรดทั้งหมดที่พยายามล็อค monitorrecord
RCTHIS: แสดงจำนวนเธรดทั้งหมดที่ถูกบล็อกหรือรอการตรวจสอบ
NEST: ใช้เพื่อใช้การนับล็อคอีกครั้ง
HashCode: บันทึกค่า HashCode ที่คัดลอกจากส่วนหัวของวัตถุ (อาจมี GCAGE)
ผู้สมัคร: ใช้เพื่อหลีกเลี่ยงการปิดกั้นที่ไม่จำเป็นหรือรอให้เธรดตื่นขึ้นมาเพราะมีเพียงเธรดเดียวเท่านั้นที่สามารถเป็นเจ้าของล็อคได้สำเร็จในแต่ละครั้ง หากเธรดก่อนหน้านี้ที่ปล่อยล็อคจะปลุกการปิดกั้นหรือการรอเธรดทั้งหมดในแต่ละครั้งมันจะทำให้การสลับบริบทที่ไม่จำเป็น (จากการปิดกั้นเป็นความพร้อมแล้วบล็อกอีกครั้งเนื่องจากความล้มเหลวของล็อคการแข่งขัน) และนำไปสู่การเสื่อมสภาพของประสิทธิภาพอย่างรุนแรง ผู้สมัครมีค่าที่เป็นไปได้เพียงสองค่าเท่านั้น: 0 หมายความว่าไม่มีเธรดที่ต้องตื่นสูงถึง 1 หมายถึงการปลุกเธรดทายาทเพื่อแข่งขันเพื่อล็อค
การอ้างอิง: พูดคุยเกี่ยวกับการซิงโครไนซ์ใน Java พร้อมกัน
เรารู้ว่าการซิงโครไนซ์เป็นล็อคเฮฟวี่เวทและประสิทธิภาพของมันไม่ค่อยดีนัก ในเวลาเดียวกันแนวคิดนี้อยู่ในใจของเราเสมอ อย่างไรก็ตามการใช้งานการซิงโครไนซ์ได้รับการปรับให้เหมาะสมใน JDK1.6 ทำให้ไม่หนักขนาดนั้น ดังนั้น JVM ใช้วิธีการเพิ่มประสิทธิภาพแบบใด?
ล็อคการเพิ่มประสิทธิภาพ
JDK1.6 แนะนำการเพิ่มประสิทธิภาพจำนวนมากเกี่ยวกับการใช้งานล็อคเช่นล็อคสปินล็อคสปินแบบปรับตัวการกำจัดล็อคล็อคความหยาบล็อคอคติล็อคน้ำหนักเบาและเทคโนโลยีอื่น ๆ เพื่อลดค่าใช้จ่ายของการล็อค
มีสี่สถานะหลักของล็อคคือ: สถานะที่ไม่มีล็อคสถานะล็อคลำเอียงสถานะล็อคน้ำหนักเบาและสถานะล็อคเฮฟวี่เวท พวกเขาจะค่อยๆอัพเกรดด้วยการแข่งขันที่ดุเดือด โปรดทราบว่าสามารถอัพเกรดล็อคและไม่สามารถลดระดับได้ กลยุทธ์นี้คือการปรับปรุงประสิทธิภาพของการได้รับและปล่อยล็อค
ล็อคหมุน
การปิดกั้นเธรดและการปลุกต้องใช้ CPU ในการเปลี่ยนจากสถานะผู้ใช้เป็นสถานะหลัก การปิดกั้นและการปลุกบ่อยครั้งเป็นงานหนักสำหรับ CPU และมันจะสร้างแรงกดดันอย่างมากต่อประสิทธิภาพการทำงานพร้อมกันของระบบอย่างหลีกเลี่ยงไม่ได้ ในเวลาเดียวกันเราพบว่าในหลายแอปพลิเคชันสถานะล็อคของการล็อควัตถุจะอยู่ได้นานในช่วงเวลาสั้น ๆ มันไม่คู่ควรที่จะบล็อกบ่อยครั้งและปลุกเธรดในช่วงเวลาสั้น ๆ นี้ ดังนั้นจึงมีการแนะนำสปินล็อค
สปินล็อคคืออะไร?
ล็อคสปินที่เรียกว่าเพื่อให้เธรดรอเป็นระยะเวลาหนึ่งและไม่ถูกระงับทันทีเพื่อดูว่าเธรดที่ถือล็อคจะปล่อยล็อคอย่างรวดเร็วหรือไม่ รอได้อย่างไร? เพียงดำเนินการวงจรที่ไม่มีความหมาย (หมุน)
การรอสปินไม่สามารถแทนที่การปิดกั้นให้พูดคุยเกี่ยวกับข้อกำหนดสำหรับจำนวนโปรเซสเซอร์ (หลายคอร์ดูเหมือนว่าตอนนี้ไม่มีโปรเซสเซอร์แบบคอร์เดี่ยว) แม้ว่าจะสามารถหลีกเลี่ยงค่าใช้จ่ายที่เกิดจากการสลับเธรด แต่ก็ใช้เวลาโปรเซสเซอร์ หากเธรดถือล็อคปล่อยล็อคอย่างรวดเร็วประสิทธิภาพการหมุนจะดีมาก ในทางตรงกันข้ามเธรดสปินจะใช้ทรัพยากรการประมวลผลอย่างไร้ผล มันจะไม่ทำงานที่มีความหมายใด ๆ โดยทั่วไปแล้วมันจะครอบครองหลุมและไม่อึซึ่งจะนำไปสู่การเสียประสิทธิภาพแทน ดังนั้นเวลาสำหรับการรอหมุน (จำนวนสปิน) จะต้องมีขีด จำกัด หากการหมุนเกินเวลาที่กำหนดและยังไม่ได้รับการล็อคควรถูกระงับ
Spinlock เปิดตัวใน JDK1.4.2 และถูกปิดตามค่าเริ่มต้น แต่สามารถเปิดใช้งานได้ด้วย -xx:+usespinning และเปิดใช้งานตามค่าเริ่มต้นใน JDK1.6 จำนวนเริ่มต้นของสปินในเวลาเดียวกันคือ 10 ครั้งซึ่งสามารถปรับได้โดยพารามิเตอร์ -xx: preblockspin;
หากคุณปรับจำนวนสปินของสปินของสปินล็อคผ่านพารามิเตอร์ -xx: preblockspin มันจะทำให้เกิดความไม่สะดวกมากมาย ถ้าฉันปรับพารามิเตอร์เป็น 10 แต่เธรดจำนวนมากในระบบจะปล่อยล็อคเมื่อคุณเพิ่งออก (ถ้าคุณหมุนหนึ่งหรือสองครั้งคุณสามารถล็อคได้) คุณจะอายหรือไม่? ดังนั้น JDK1.6 จึงแนะนำล็อคหมุนแบบปรับตัวทำให้เครื่องเสมือนฉลาดและฉลาดขึ้น
ปรับสปินล็อค
JDK1.6 แนะนำล็อคหมุนอย่างชาญฉลาดคือล็อคสปินแบบปรับตัว การปรับตัวที่เรียกว่าหมายความว่าจำนวนสปินไม่ได้รับการแก้ไขอีกต่อไปมันถูกกำหนดโดยเวลาหมุนบนล็อคเดียวกันและสถานะของเจ้าของล็อค ทำอย่างไร? หากเธรดหมุนได้สำเร็จจำนวนสปินจะเพิ่มขึ้นในครั้งต่อไปเนื่องจากเครื่องเสมือนจริงเชื่อว่าตั้งแต่ครั้งสุดท้ายที่ประสบความสำเร็จการหมุนจะประสบความสำเร็จอีกครั้งและจะช่วยให้สปินรอได้มากขึ้น ในทางตรงกันข้ามหากสปินเพียงไม่กี่ตัวที่สามารถประสบความสำเร็จได้สำหรับการล็อคบางอย่างจำนวนสปินจะลดลงหรือถูกละเว้นเมื่อจำเป็นต้องมีการล็อคในอนาคตเพื่อไม่ให้ทรัพยากรของโปรเซสเซอร์เสีย
ด้วยการล็อคสปินแบบปรับตัวในขณะที่การดำเนินการโปรแกรมและข้อมูลการตรวจสอบประสิทธิภาพยังคงปรับปรุงอย่างต่อเนื่องการทำนายสถานะการล็อคโปรแกรมเสมือนจริงของเครื่องจะมีความแม่นยำมากขึ้นเรื่อย ๆ และเครื่องเสมือนจะฉลาดขึ้น
การขจัดล็อค
เพื่อให้แน่ใจว่ามีความสมบูรณ์ของข้อมูลเราจำเป็นต้องซิงโครไนซ์ส่วนนี้ของการดำเนินการเมื่อดำเนินการ แต่ในบางกรณี JVM ตรวจพบว่าไม่มีความเป็นไปได้ของการแข่งขันข้อมูลที่ใช้ร่วมกันซึ่งเป็นสาเหตุที่ JVM จะล็อคล็อคการซิงโครไนซ์เหล่านี้ พื้นฐานสำหรับการกำจัดล็อคคือการสนับสนุนข้อมูลสำหรับการวิเคราะห์หลบหนี
หากไม่มีการแข่งขันทำไมคุณยังต้องเพิ่มการล็อค? ดังนั้นการกำจัดล็อคสามารถประหยัดเวลาได้อย่างไม่มีความหมายว่าขอล็อค ไม่ว่าตัวแปรการหลบหนีจะจำเป็นสำหรับเครื่องเสมือนเพื่อตรวจสอบว่าใช้การวิเคราะห์การไหลของข้อมูลหรือไม่ แต่ก็ยังไม่ชัดเจนสำหรับโปรแกรมเมอร์ของสหรัฐอเมริกาหรือไม่? เราจะเพิ่มการซิงโครไนซ์ก่อนบล็อกรหัสที่รู้อย่างชัดเจนว่าไม่มีการแข่งขันข้อมูลหรือไม่? แต่บางครั้งโปรแกรมไม่ใช่สิ่งที่เราคิด? แม้ว่าเราจะไม่แสดงการล็อค แต่เมื่อเราใช้ API ในตัว JDK บางตัวเช่น StringBuffer, Vector, Hashtable ฯลฯ จะมีการล็อคที่มองไม่เห็นในเวลานี้ ตัวอย่างเช่นเมธอด Append () ของ StringBuffer และวิธีการเพิ่มของ Vector's Add ():
โมฆะสาธารณะ vectortest () {เวกเตอร์ <String> เวกเตอร์ = เวกเตอร์ใหม่ <String> (); สำหรับ (int i = 0; i <10; i ++) {vector.add (i+""); } system.out.println (เวกเตอร์); -เมื่อเรียกใช้รหัสนี้ JVM สามารถตรวจจับได้อย่างชัดเจนว่าเวกเตอร์ตัวแปรไม่หลบหนีจากวิธีการ vectortest () ดังนั้น JVM สามารถกำจัดการดำเนินการล็อคภายในเวกเตอร์ได้อย่างกล้าหาญ
ล็อคการหยาบกร้าน
เรารู้ว่าเมื่อใช้การล็อคการซิงโครไนซ์ขอบเขตของบล็อกการซิงโครไนซ์จะต้องมีขนาดเล็กที่สุดเท่าที่จะทำได้ - ซิงโครไนซ์ในขอบเขตจริงของข้อมูลที่ใช้ร่วมกัน วัตถุประสงค์ของสิ่งนี้คือเพื่อลดจำนวนการดำเนินการที่ต้องซิงโครไนซ์ให้มากที่สุด หากมีการแข่งขันล็อคเธรดที่รอล็อคสามารถล็อคได้โดยเร็วที่สุด
ในกรณีส่วนใหญ่มุมมองข้างต้นถูกต้องและ LZ ได้ปฏิบัติตามมุมมองนี้เสมอ อย่างไรก็ตามหากชุดของการล็อคและการปลดล็อคอย่างต่อเนื่องอาจนำไปสู่การสูญเสียประสิทธิภาพที่ไม่จำเป็นดังนั้นจึงมีการแนะนำแนวคิดของการล็อคเทียม
แนวคิดของการใส่ร้ายล็อคนั้นง่ายต่อการเข้าใจซึ่งคือการเชื่อมต่อการล็อคอย่างต่อเนื่องและการปลดล็อคการดำเนินการเข้าด้วยกันและขยายเข้าไปในล็อคที่มีระยะไกล ดังที่แสดงในตัวอย่างข้างต้น: เวกเตอร์ต้องเพิ่มการดำเนินการล็อคทุกครั้งที่เพิ่ม JVM ตรวจพบว่าวัตถุเดียวกัน (เวกเตอร์) ถูกล็อคและปลดล็อคอย่างต่อเนื่องและจะรวมการดำเนินการที่ล็อคและปลดล็อคช่วงที่ใหญ่กว่านั่นคือการดำเนินการล็อคและปลดล็อคจะถูกย้ายออกไปนอกลูป
ล็อคน้ำหนักเบา
วัตถุประสงค์หลักของการแนะนำล็อคน้ำหนักเบาคือการลดการใช้ประสิทธิภาพที่เกิดจากการใช้ระบบปฏิบัติการ mutexes ภายใต้หลักฐานของการแข่งขันหลายครั้งโดยไม่มีหลายเธรด เมื่อฟังก์ชั่นล็อคอคติถูกปิดหรือหลายเธรดจะแข่งขันเพื่อล็อคอคติการล็อคอคติจะถูกอัพเกรดเป็นล็อคที่มีน้ำหนักเบาจากนั้นล็อคน้ำหนักเบาจะถูกพยายามและขั้นตอนดังต่อไปนี้:
รับล็อค
ตรวจสอบว่าวัตถุปัจจุบันอยู่ในสถานะปราศจากล็อค (HashCode, 0, 01) ถ้าเป็นเช่นนั้น JVM จะสร้างพื้นที่ที่เรียกว่าบันทึกล็อคในเฟรมสแต็กของเธรดปัจจุบันเพื่อจัดเก็บสำเนาคำทำเครื่องหมายปัจจุบันของวัตถุล็อค (อย่างเป็นทางการเพิ่มคำนำหน้าแบบแทนที่ลงในสำเนานี้คือคำทำเครื่องหมายที่ถูกแทนที่); มิฉะนั้นให้ดำเนินการขั้นตอน (3);
JVM ใช้การดำเนินการ CAS เพื่อพยายามอัปเดตคำทำเครื่องหมายของวัตถุไปยังการแก้ไขที่ชี้ไปที่ล็อคเร็กคอร์ด หากประสบความสำเร็จบ่งชี้ว่าล็อคถูกโต้แย้งธงล็อคจะเปลี่ยนเป็น 00 (ระบุว่าวัตถุอยู่ในสถานะล็อคน้ำหนักเบา) และดำเนินการซิงโครไนซ์ หากล้มเหลวให้ดำเนินการขั้นตอน (3);
ตรวจสอบว่าคำทำเครื่องหมายของวัตถุปัจจุบันชี้ไปที่เฟรมสแต็กของเธรดปัจจุบันหรือไม่ ถ้าเป็นเช่นนั้นหมายความว่าเธรดปัจจุบันจะถือล็อคของวัตถุปัจจุบันแล้วจากนั้นเรียกใช้งานบล็อกรหัสซิงโครนัสโดยตรง มิฉะนั้นอาจหมายความว่าวัตถุล็อคได้ถูกยึดโดยเธรดอื่น ในเวลานี้ล็อคที่มีน้ำหนักเบาจะต้องขยายเข้าไปในล็อคเฮฟวี่เวทบิตธงล็อคจะกลายเป็น 10 และเธรดที่รอในภายหลังจะเข้าสู่สถานะการบล็อก
ปล่อยล็อค
การเปิดตัวของล็อคน้ำหนักเบานั้นดำเนินการผ่านการดำเนินการ CAS และขั้นตอนหลักมีดังนี้:
ลบข้อมูลที่บันทึกไว้ในคำทำเครื่องหมายที่ถูกแทนที่ในการล็อคน้ำหนักเบา
แทนที่ข้อมูลที่ดึงมาเป็นคำทำเครื่องหมายด้วยการดำเนินการ CAS หากประสบความสำเร็จก็หมายความว่าการล็อคจะถูกปล่อยออกมาสำเร็จไม่เช่นนั้นจะถูกดำเนินการ (3);
หากการเปลี่ยนการดำเนินการ CAS ล้มเหลวหมายความว่าเธรดอื่น ๆ พยายามที่จะได้รับการล็อคและต้องตื่นเธรดที่ถูกระงับในขณะที่ปล่อยล็อค
สำหรับล็อคที่มีน้ำหนักเบาพื้นฐานสำหรับการปรับปรุงประสิทธิภาพคือ "สำหรับล็อคส่วนใหญ่จะไม่มีการแข่งขันตลอดวงจรชีวิตทั้งหมด" หากพื้นฐานนี้เสียนอกเหนือจากค่าใช้จ่ายพิเศษร่วมกันแล้วยังมีการดำเนินการ CAS เพิ่มเติม ดังนั้นในกรณีของการแข่งขันแบบมัลติเธรดล็อคน้ำหนักเบาช้ากว่าล็อคเฮฟวี่เวท
รูปต่อไปนี้แสดงกระบวนการซื้อและการปล่อยของล็อคน้ำหนักเบา
ล็อคบวก
วัตถุประสงค์หลักของการแนะนำล็อคลำเอียงคือการลดเส้นทางการดำเนินการล็อคน้ำหนักเบาที่ไม่จำเป็นโดยไม่ต้องมีการแข่งขันแบบมัลติเธรด ดังกล่าวข้างต้นว่าการล็อคและปลดล็อคการดำเนินการล็อคน้ำหนักเบาต้องใช้คำแนะนำในการทำอะตอม CAS หลายคำ ดังนั้น Bias Lock จะลดการดำเนินงาน CAS ที่ไม่จำเป็นได้อย่างไร เราสามารถเห็นโครงสร้างของการทำงานของเครื่องหมาย คุณจะต้องตรวจสอบว่ามันเป็นล็อคลำเอียงหรือไม่ล็อคจะถูกระบุว่าเป็นและ threadID กระแสการประมวลผลมีดังนี้:
รับล็อค
ตรวจพบว่าเครื่องหมายของมาร์คอยู่ในสถานะที่น่าเบื่อหน่ายหรือไม่นั่นคือไม่ว่าจะเป็นล็อคลำเอียง 1 และบิตการระบุล็อคคือ 01;
หากเป็นสถานะที่มีคุณสมบัติได้ให้ทดสอบว่า ID เธรดเป็น ID เธรดปัจจุบันหรือไม่ ถ้าเป็นเช่นนั้นให้ดำเนินการขั้นตอน (5) มิฉะนั้นจะดำเนินการขั้นตอน (3);
หาก ID เธรดไม่ใช่ ID เธรดปัจจุบันการล็อคจะถูกแข่งขันโดยการดำเนินการ CAS หากการแข่งขันประสบความสำเร็จ ID เธรดของคำทำเครื่องหมายจะถูกแทนที่ด้วยรหัสเธรดปัจจุบันมิฉะนั้นเธรด (4);
การแข่งขัน CAS Lock ล้มเหลวซึ่งพิสูจน์ให้เห็นว่าปัจจุบันมีสถานการณ์การแข่งขันแบบมัลติเธรด เมื่อถึงจุดรักษาความปลอดภัยทั่วโลกเธรดที่ได้รับการล็อคแบบเอนเอียงจะถูกระงับการล็อคลำเอียงจะถูกอัพเกรดเป็นล็อคที่มีน้ำหนักเบาและจากนั้นเธรดที่ถูกบล็อกที่จุดรักษาความปลอดภัยยังคงดำเนินการบล็อกรหัสซิงโครนัส
ดำเนินการบล็อกรหัสแบบซิงโครนัส
ปล่อยล็อค การเปิดตัวของการล็อคลำเอียงใช้กลไกที่มีเพียงการแข่งขันเท่านั้นที่สามารถปล่อยล็อคได้ เธรดจะไม่ปล่อยล็อคลำเอียงอย่างแข็งขันและจำเป็นต้องรอให้เธรดอื่น ๆ แข่งขันกัน การยกเลิกการล็อคลำเอียงต้องรอจุดรักษาความปลอดภัยทั่วโลก (จุดนี้คือไม่มีรหัสที่ถูกดำเนินการที่) ขั้นตอนมีดังนี้:
หยุดด้ายชั่วคราวด้วยล็อคลำเอียงและตรวจสอบว่าหินวัตถุล็อคยังอยู่ในสถานะล็อคหรือไม่
ปลดล็อกอคติต่อ SU และกู้คืนสู่สถานะฟรีล็อค (01) หรือสถานะล็อคที่มีน้ำหนักเบา
รูปต่อไปนี้แสดงกระบวนการซื้อและการปล่อยของล็อคลำเอียง
ล็อคเฮฟวี่เวท
การล็อคน้ำหนักเรียกอีกอย่างว่าการตรวจสอบวัตถุ (จอภาพ) ใน JVM มันคล้ายกับ mutex ใน C. นอกเหนือจากการมีฟังก์ชั่นพิเศษ Mutex (0 | 1) มันยังรับผิดชอบในการใช้ฟังก์ชันของเซมาฟอร์นั่นคือมันมีอย่างน้อยคิวล็อคการแข่งขันและคิวการปิดกั้นสัญญาณ (รอคิว) อดีตมีหน้าที่รับผิดชอบในการยกเว้นซึ่งกันและกันและหลังใช้สำหรับการซิงโครไนซ์เธรด
ล็อคเฮฟวี่เวทจะถูกนำไปใช้ผ่านจอภาพ (จอภาพ) ภายในวัตถุซึ่งสาระสำคัญของการตรวจสอบคือการพึ่งพาการใช้งานล็อค Mutex ของระบบปฏิบัติการพื้นฐาน การสลับระหว่างเธรดของระบบปฏิบัติการจำเป็นต้องเปลี่ยนจากสถานะผู้ใช้เป็นสถานะเคอร์เนลและค่าใช้จ่ายในการสลับสูงมาก
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้เกี่ยวกับคำอธิบายโดยละเอียดของหลักการดำเนินการของการซิงโครไนซ์ใน Java ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน หากมีข้อบกพร่องใด ๆ โปรดฝากข้อความไว้เพื่อชี้ให้เห็น ขอบคุณเพื่อนที่ให้การสนับสนุนเว็บไซต์นี้!