ก่อนอื่นมาแนะนำล็อคในแง่ดีและล็อคในแง่ร้าย:
ล็อคในแง่ร้าย: สมมติว่าเป็นกรณีที่เลวร้ายที่สุดเสมอ ทุกครั้งที่ฉันไปรับข้อมูลฉันคิดว่าคนอื่นจะแก้ไขดังนั้นฉันจะล็อคมันทุกครั้งที่ได้รับข้อมูลเพื่อให้คนอื่น ๆ จะบล็อกมันจนกว่าจะได้รับการล็อค ฐานข้อมูลเชิงสัมพันธ์แบบดั้งเดิมใช้กลไกการล็อคหลายอย่างเช่นล็อคแถวล็อคตาราง ฯลฯ อ่านล็อคเขียนล็อค ฯลฯ ซึ่งถูกล็อคก่อนทำการดำเนินการ ตัวอย่างเช่นการใช้คำหลักที่ซิงโครไนซ์คำหลักที่ซิงโครไนซ์ใน Java ยังเป็นการล็อคในแง่ร้าย
การล็อคในแง่ดี: ตามชื่อแนะนำมันหมายถึงแง่ดีมาก ทุกครั้งที่ฉันไปรับข้อมูลฉันคิดว่าคนอื่นจะไม่แก้ไขดังนั้นฉันจะไม่ล็อค อย่างไรก็ตามเมื่ออัปเดตฉันจะตัดสินว่าคนอื่นได้อัปเดตข้อมูลในช่วงเวลานี้หรือไม่และสามารถใช้หมายเลขเวอร์ชันและกลไกอื่น ๆ ได้หรือไม่ ล็อคในแง่ดีเหมาะสำหรับแอปพลิเคชันแบบมัลติอ่านซึ่งสามารถปรับปรุงปริมาณงาน ตัวอย่างเช่นฐานข้อมูลให้การล็อคในแง่ดีคล้ายกับกลไก WRITE_CONDITION แต่จริง ๆ แล้วล็อคทั้งหมดได้รับจากการล็อคในแง่ดี ใน Java คลาสตัวแปรอะตอมภายใต้ java.util.concurrent.atomic แพ็คเกจถูกนำมาใช้โดย CA โดยใช้การล็อคในแง่ดี
การใช้งาน Lock-Cas ในแง่ดี (เปรียบเทียบและแลกเปลี่ยน):
ปัญหาล็อค:
ก่อน JDK1.5 Java อาศัยคำหลักที่ซิงโครไนซ์เพื่อให้แน่ใจว่าการซิงโครไนซ์ ด้วยวิธีนี้โดยใช้โปรโตคอลล็อคที่สอดคล้องกันเพื่อประสานการเข้าถึงสถานะที่ใช้ร่วมกันสามารถตรวจสอบให้แน่ใจว่าไม่ว่าเธรดใดที่จะล็อคตัวแปรที่ใช้ร่วมกันมันใช้วิธีพิเศษในการเข้าถึงตัวแปรเหล่านี้ นี่คือล็อคแบบพิเศษ ล็อคพิเศษเป็นล็อคในแง่ร้ายดังนั้นจึงสามารถกล่าวได้ว่าการซิงโครไนซ์เป็นล็อคในแง่ร้าย
กลไกการล็อคในแง่ร้ายมีปัญหาดังต่อไปนี้:
1. ภายใต้การแข่งขันแบบมัลติเธรดการเพิ่มและการปล่อยล็อคจะนำไปสู่การสลับบริบทมากขึ้นและความล่าช้าในการกำหนดเวลาทำให้เกิดปัญหาด้านประสิทธิภาพ
2. เธรดที่ถือล็อคจะทำให้เธรดอื่น ๆ ทั้งหมดที่ต้องการล็อคนี้เพื่อแขวน
3. หากเธรดที่มีลำดับความสำคัญสูงรอด้ายที่มีลำดับความสำคัญต่ำในการปล่อยล็อคมันจะทำให้เกิดการผกผันลำดับความสำคัญทำให้เกิดความเสี่ยงด้านประสิทธิภาพ
เมื่อเทียบกับปัญหาเหล่านี้ของการล็อคในแง่ร้ายการล็อคที่มีประสิทธิภาพอีกอย่างคือล็อคในแง่ดี ในความเป็นจริงการล็อคในแง่ดีคือ: ทุกครั้งที่คุณไม่ได้เพิ่มการล็อค แต่คุณเสร็จสิ้นการดำเนินการโดยสมมติว่าไม่มีความขัดแย้งพร้อมกัน หากความขัดแย้งพร้อมกันล้มเหลวลองอีกครั้งจนกว่าจะประสบความสำเร็จ
การล็อคในแง่ดี:
การล็อคในแง่ดีได้รับการกล่าวถึงข้างต้น แต่จริงๆแล้วมันเป็นความคิดแบบนี้ เมื่อเปรียบเทียบกับการล็อคในแง่ร้ายล็อคในแง่ดีสันนิษฐานว่าข้อมูลโดยทั่วไปจะไม่ก่อให้เกิดความขัดแย้งพร้อมกันดังนั้นเมื่อมีการส่งและอัปเดตข้อมูลจะตรวจพบอย่างเป็นทางการว่าข้อมูลมีความขัดแย้งพร้อมกันหรือไม่ หากพบความขัดแย้งพร้อมกันข้อมูลผิดของผู้ใช้จะถูกส่งคืนและผู้ใช้ตัดสินใจว่าจะทำอย่างไร
แนวคิดของการล็อคในแง่ดีที่กล่าวถึงข้างต้นได้อธิบายรายละเอียดการใช้งานที่เฉพาะเจาะจงจริง ๆ : ส่วนใหญ่มีสองขั้นตอน: การตรวจจับความขัดแย้งและการอัปเดตข้อมูล หนึ่งในวิธีการใช้งานทั่วไปคือการเปรียบเทียบและแลกเปลี่ยน (CAS)
Cas:
CAS เป็นเทคโนโลยีการล็อคในแง่ดี เมื่อหลายเธรดพยายามใช้ CAS เพื่ออัปเดตตัวแปรเดียวกันในเวลาเดียวกันมีเพียงหนึ่งเธรดเท่านั้นที่สามารถอัปเดตค่าของตัวแปรในขณะที่เธรดอื่น ๆ ล้มเหลว เธรดที่ล้มเหลวจะไม่ถูกระงับ แต่จะบอกว่าการแข่งขันครั้งนี้ล้มเหลวและสามารถลองอีกครั้ง
การดำเนินการ CAS ประกอบด้วยตัวถูกดำเนินการสามตัว - ตำแหน่งหน่วยความจำ (v) ที่ต้องอ่านและเขียนค่าดั้งเดิมที่คาดหวัง (a) สำหรับการเปรียบเทียบและค่าใหม่ (b) ที่จะเขียน หากค่าของตำแหน่งหน่วยความจำ V ตรงกับค่าดั้งเดิมที่คาดหวัง A โปรเซสเซอร์จะอัปเดตค่าตำแหน่งเป็นค่าใหม่โดยอัตโนมัติ B โปรเซสเซอร์จะไม่ทำอะไรเลย ไม่ว่าในกรณีใดมันจะส่งคืนค่าของตำแหน่งนั้นก่อนคำสั่ง CAS (ในบางกรณีพิเศษของ CAS ไม่ว่าจะประสบความสำเร็จหรือไม่โดยไม่ต้องแยกค่าปัจจุบัน) CAS ระบุอย่างมีประสิทธิภาพว่า "ฉันคิดว่าตำแหน่ง V ควรมีค่า A; ถ้ามีอยู่ให้ใส่ B ไว้ในตำแหน่งนี้; นี่เป็นจริงเช่นเดียวกับหลักการของการตรวจสอบความขัดแย้ง + การอัปเดตข้อมูลของการล็อคในแง่ดี
ให้ฉันเน้นที่นี่ว่าการล็อคในแง่ดีเป็นความคิด CAS เป็นวิธีการตระหนักถึงความคิดนี้
การสนับสนุน Java สำหรับ CAS:
java.util.concurrent (JUC) ใหม่ใน JDK1.5 ถูกสร้างขึ้นบน CAS เมื่อเปรียบเทียบกับอัลกอริทึมการปิดกั้นเช่นการซิงโครไนซ์ CAS เป็นการดำเนินการทั่วไปของอัลกอริทึมที่ไม่ปิดกั้น ดังนั้น JUC จึงปรับปรุงประสิทธิภาพอย่างมาก
ใช้ atomicinteger ใน java.util.concurrent เป็นตัวอย่างเพื่อดูวิธีการให้แน่ใจว่ามีความปลอดภัยของเธรดโดยไม่ต้องใช้ล็อค เราเข้าใจวิธีการ getandincrement เป็นหลักซึ่งเทียบเท่ากับการดำเนินการ ++ I
Atomicinteger ระดับสาธารณะขยายจำนวนใช้งาน java.io.serializable {ค่า int ผันผวนส่วนตัว; Public Final Int Get () {มูลค่าคืน; } สาธารณะ int สุดท้าย getandincrement () {สำหรับ (;;) {int current = get (); int next = current + 1; ถ้า (เปรียบเทียบ (ปัจจุบัน, ถัดไป)) ส่งคืนกระแส; }} public Boolean Final PomperEndset (int คาดหวัง, การอัปเดต int) {return unsafe.compareandswapint (นี่, valueOffset, คาดหวัง, อัปเดต); -ในกลไกที่ไม่มีล็อคค่าฟิลด์จะต้องใช้เพื่อให้แน่ใจว่าข้อมูลระหว่างเธรดคือการมองเห็น ด้วยวิธีนี้คุณสามารถอ่านได้โดยตรงเมื่อคุณได้รับค่าของตัวแปร จากนั้นมาดูกันว่า ++ ฉันทำอย่างไร
Getandincrement ใช้การดำเนินการ CAS และทุกครั้งที่คุณอ่านข้อมูลจากหน่วยความจำจากนั้นดำเนินการ CAS ในข้อมูลนี้และผลลัพธ์หลังจาก +1 หากประสบความสำเร็จผลลัพธ์จะถูกส่งคืนมิฉะนั้นลองอีกครั้งจนกว่าจะประสบความสำเร็จ
การเปรียบเทียบใช้ JNI (อินเตอร์เฟส Java Native) เพื่อดำเนินการตามคำแนะนำของ CPU:
Public Final Boolean PomperEandEndset (Int คาดหวัง, Int Update) {return unsafe.compareandswapint (สิ่งนี้, valueOffset, คาดหวัง, อัปเดต); -โดยที่ unsafe.compareandswapint (นี่, valueOffset, คาดหวัง, อัปเดต); คล้ายกับตรรกะต่อไปนี้:
if (this == คาดหวัง) {this = update return true; } else {return false; -ดังนั้นวิธีการเปรียบเทียบสิ่งนี้ == คาดหวังให้แทนที่ = การอัปเดตนี้, เปรียบเทียบและการเปรียบเทียบเพื่อให้บรรลุความเป็นอะตอมของสองขั้นตอนนี้? อ้างถึงหลักการของ CAS
หลักการ CAS:
CAS ถูกนำไปใช้โดยการโทรรหัส JNI เปรียบเทียบและ WAPINT ถูกนำมาใช้โดยใช้ C เพื่อเรียกคำแนะนำ CPU พื้นฐาน
ต่อไปนี้อธิบายหลักการการดำเนินงานของ CAS จากการวิเคราะห์ CPU ที่ใช้กันทั่วไปมากขึ้น (Intel X86)
นี่คือซอร์สโค้ดของวิธีการเปรียบเทียบและ () ของคลาส Sun.misc.unsafe:
Public Native Native Boolean Sompereandswapint (Object O, Offset Long, Int คาดหวัง, int x);
คุณจะเห็นว่านี่เป็นวิธีการโทรท้องถิ่น รหัส C ++ ที่วิธีการในท้องถิ่นนี้เรียกใน JDK คือ:
#Define LOCK_IF_MP (MP) __ASM CMMP MP, 0 / __ASM JE L0 / __ASM _EMIT 0XF0 / __ASM L0: Inline JINT ATOMIC :: CMPXCHG OS :: IS_MP (); __ASM {mov edx, dest mov ecx, exchange_value mov eax, compare_value lock_if_mp (mp) cmmpxchg dword ptr [edx], ecx}}ดังที่แสดงในซอร์สโค้ดด้านบนโปรแกรมจะตัดสินใจว่าจะเพิ่มคำนำหน้าล็อคลงในคำสั่ง CMMPXCHG ตามประเภทของโปรเซสเซอร์ปัจจุบันหรือไม่ หากโปรแกรมทำงานบนมัลติโปรเซสเซอร์ให้เพิ่มคำนำหน้าล็อคลงในคำสั่ง CMMPXCHG ในทางตรงกันข้ามหากโปรแกรมทำงานบนโปรเซสเซอร์เดียวคำนำหน้าล็อคจะถูกละเว้น (โปรเซสเซอร์เดียวเองจะรักษาความสอดคล้องตามลำดับภายในโปรเซสเซอร์เดียวและไม่จำเป็นต้องมีเอฟเฟกต์หน่วยความจำที่ได้รับจากคำนำหน้าล็อค)
ข้อเสียของ CAS:
1. คำถาม ABA:
ตัวอย่างเช่นหากเธรดหนึ่งออกมาจากตำแหน่งหน่วยความจำ V แล้วเธรดอื่นสองก็จะออกมาจากหน่วยความจำและสองดำเนินการบางอย่างและกลายเป็น B จากนั้นสองจะหมุนข้อมูลที่ตำแหน่ง V ในเวลานี้เธรดหนึ่งจะดำเนินการ CAS และพบว่ายังอยู่ในหน่วยความจำ แม้ว่าการดำเนินการของเธรดจะประสบความสำเร็จ แต่อาจมีปัญหาที่ซ่อนอยู่ ดังที่แสดงด้านล่าง:
มีสแต็กที่ใช้งานกับรายการที่เชื่อมโยงทางเดียวโดยด้านบนของสแต็กเป็น A. ในเวลานี้เธรด T1 รู้อยู่แล้วว่า A.Next คือ B แล้วหวังว่าจะแทนที่ด้านบนของสแต็กด้วย B ด้วย CAS: CAS:
head.compareandset (a, b);
ก่อนที่ T1 จะดำเนินการคำสั่งด้านบนเธรด T2 แทรกแซงวาง A และ B ออกจากสแต็กแล้ว pushd, C และ A ในเวลานี้โครงสร้างสแต็กมีดังนี้และ Object B อยู่ในสถานะอิสระในเวลานี้:
ในเวลานี้เป็นตาของเธรด T1 ที่จะดำเนินการ CAS การตรวจจับพบว่าด้านบนของสแต็คยังคงเป็น A ดังนั้น CAS จึงประสบความสำเร็จและด้านบนของสแต็กกลายเป็น B แต่ในความเป็นจริง B.Next เป็นโมฆะดังนั้นสถานการณ์ในเวลานี้จะกลายเป็น:
มีเพียงองค์ประกอบหนึ่ง B ในสแต็กและรายการที่เชื่อมโยงซึ่งประกอบด้วย C และ D ไม่มีอยู่ในสแต็กอีกต่อไป C และ D ถูกโยนทิ้งไปโดยไม่มีเหตุผล
เริ่มต้นจาก Java 1.5 แพ็คเกจอะตอมของ JDK ให้การศึกษาเกี่ยวกับ AtomicStampedReference ในชั้นเรียนเพื่อแก้ปัญหา ABA วิธีการเปรียบเทียบของคลาสนี้คือการตรวจสอบก่อนว่าการอ้างอิงปัจจุบันมีค่าเท่ากับการอ้างอิงที่คาดหวังหรือไม่และการตั้งค่าสถานะปัจจุบันเท่ากับค่าสถานะที่คาดหวังหรือไม่ หากทั้งหมดเท่ากันการอ้างอิงและค่าของธงจะถูกตั้งค่าเป็นค่าที่ได้รับการปรับปรุงในลักษณะอะตอม
Public Boolean PomperEndset (V ที่คาดว่าจะได้รับ, // การอ้างอิงที่คาดหวัง v newReference, // ปรับปรุงการอ้างอิง int ที่คาดหวัง, // ที่คาดหวังว่า Flag int Newstamp // updated Flag)
รหัสแอปพลิเคชันจริง:
Private Static AtomicStampedReference <Integer> AtomicStampedRef = ใหม่ AtomicStampedReference <Integer> (100, 0); ......... Atomicstampedref.Compareandset (100, 101, แสตมป์, แสตมป์ + 1);
2. รอบเวลายาวและค่าใช้จ่ายสูง:
สปิน CAS (ถ้ามันล้มเหลวมันจะถูกดำเนินการขี่จักรยานจนกว่าจะประสบความสำเร็จ) ถ้ามันล้มเหลวเป็นเวลานานมันจะนำค่าใช้จ่ายที่ยอดเยี่ยมไปยัง CPU หาก JVM สามารถรองรับคำแนะนำหยุดชั่วคราวที่ให้ไว้โดยโปรเซสเซอร์ประสิทธิภาพจะได้รับการปรับปรุงในระดับหนึ่ง คำแนะนำหยุดชั่วคราวมีสองฟังก์ชั่น ก่อนอื่นสามารถชะลอคำแนะนำการดำเนินการท่อ (de-pipeline) เพื่อให้ CPU จะไม่ใช้ทรัพยากรการดำเนินการมากเกินไป เวลาหน่วงขึ้นอยู่กับรุ่นการใช้งานเฉพาะ ในโปรเซสเซอร์บางตัวเวลาหน่วงเป็นศูนย์ ประการที่สองมันสามารถหลีกเลี่ยงการล้างท่อ CPU ที่เกิดจากการละเมิดคำสั่งหน่วยความจำเมื่อออกจากลูปซึ่งจะเป็นการปรับปรุงประสิทธิภาพการดำเนินการของ CPU
3. การดำเนินการอะตอมของตัวแปรที่ใช้ร่วมกันเท่านั้นที่สามารถรับประกันได้:
เมื่อดำเนินการกับตัวแปรที่ใช้ร่วมกันเราสามารถใช้วิธี Cyclic CAS เพื่อให้แน่ใจว่าการทำงานของอะตอม อย่างไรก็ตามเมื่อใช้งานตัวแปรที่ใช้ร่วมกันหลายตัว CAS CYCLIC ไม่สามารถรับประกันความเป็นอะตอมของการดำเนินการได้ ในเวลานี้คุณสามารถใช้ล็อคหรือมีเคล็ดลับซึ่งคือการรวมตัวแปรที่ใช้ร่วมกันหลายตัวเข้ากับตัวแปรที่ใช้ร่วมกันเพื่อทำงาน ตัวอย่างเช่นมีตัวแปรที่ใช้ร่วมกันสองตัว i = 2, j = a, ผสาน IJ = 2a จากนั้นใช้ CAS เพื่อใช้งาน IJ เริ่มต้นจาก Java 1.5, JDK จัดเตรียมคลาส Atomicreference เพื่อให้แน่ใจว่าอะตอมระหว่างวัตถุที่อ้างอิง คุณสามารถใส่ตัวแปรหลายตัวในวัตถุเดียวสำหรับการดำเนินการ CAS
สถานการณ์การใช้งาน CAS และซิงโครไนซ์:
1. สำหรับสถานการณ์ที่มีการแข่งขันทรัพยากรน้อยลง (ความขัดแย้งของเธรดแสง) โดยใช้ล็อคการซิงโครไนซ์ซิงโครไนซ์สำหรับการปิดกั้นเธรดและการสลับการปลุกและการสลับการดำเนินการระหว่างสถานะเคอร์เนลของผู้ใช้รัฐคือการสูญเสียทรัพยากร CPU เป็นพิเศษ ในขณะที่ CAS ถูกนำไปใช้ตามฮาร์ดแวร์ไม่จำเป็นต้องป้อนเคอร์เนลไม่จำเป็นต้องเปลี่ยนเธรดและโอกาสในการใช้งานสปินน้อยกว่าดังนั้นประสิทธิภาพที่สูงขึ้นจึงสามารถรับประสิทธิภาพที่สูงขึ้นได้
2. สำหรับสถานการณ์ที่การแข่งขันทรัพยากรร้ายแรง (ความขัดแย้งเธรดรุนแรง) ความน่าจะเป็นของการหมุน CAS ค่อนข้างสูงซึ่งเสียทรัพยากร CPU มากขึ้นและมีประสิทธิภาพน้อยกว่าการซิงโครไนซ์
ภาคผนวก: ซิงโครไนซ์ได้รับการปรับปรุงและปรับให้เหมาะสมหลังจาก JDK1.6 การใช้งานพื้นฐานของการซิงโครไนซ์ส่วนใหญ่ขึ้นอยู่กับคิวของการล็อคฟรี แนวคิดพื้นฐานคือการปิดกั้นหลังจากสปินให้แข่งขันต่อเพื่อล็อคหลังจากสลับการแข่งขันการเสียสละความยุติธรรมเล็กน้อย แต่ได้รับปริมาณงานสูง เมื่อมีความขัดแย้งของเธรดน้อยลงประสิทธิภาพที่คล้ายกันสามารถรับได้ เมื่อมีความขัดแย้งในเธรดร้ายแรงประสิทธิภาพจะสูงกว่าของ CAS มาก
การใช้งานแพคเกจพร้อมกัน:
เนื่องจาก CAS ของ Java มีทั้งความหมายหน่วยความจำสำหรับการอ่านที่ผันผวนและการเขียนผันผวนตอนนี้มีสี่วิธีในการสื่อสารระหว่างกระทู้ Java:
1. เธรด A เขียนตัวแปรระเหยง่ายจากนั้นเธรด B จะอ่านตัวแปรระเหยง่าย
2. เธรด A เขียนตัวแปรผันผวนจากนั้นเธรด B ใช้ CAS เพื่ออัปเดตตัวแปรผันผวน
3. เธรด A ใช้ CAS เพื่ออัปเดตตัวแปรผันผวนจากนั้นเธรด B ใช้ CAS เพื่ออัปเดตตัวแปรผันผวนนี้
4. เธรด A ใช้ CAS เพื่ออัปเดตตัวแปรผันผวนจากนั้นเธรด B จะอ่านตัวแปรระเหยง่ายนี้
CAS ของ Java ใช้คำแนะนำอะตอมระดับเครื่องจักรที่มีประสิทธิภาพที่มีให้กับโปรเซสเซอร์ที่ทันสมัยซึ่งทำการดำเนินการอ่าน-เปลี่ยน-เขียนในหน่วยความจำแบบอะตอมซึ่งเป็นกุญแจสำคัญในการบรรลุการซิงโครไนซ์ในตัวประมวลผลหลายตัว (โดยหลักแล้วเครื่องคอมพิวเตอร์ คำแนะนำที่สามารถดำเนินการเกี่ยวกับการอ่านการอ่านแบบอะตอมในหน่วยความจำ) ในเวลาเดียวกันการอ่าน/เขียนและ CAS ของตัวแปรผันผวนสามารถตระหนักถึงการสื่อสารระหว่างเธรด การรวมคุณสมบัติเหล่านี้เข้าด้วยกันเป็นรากฐานที่สำคัญของการใช้งานแพ็คเกจพร้อมกันทั้งหมด หากเราวิเคราะห์การใช้งานซอร์สโค้ดอย่างรอบคอบของแพ็คเกจพร้อมกันเราจะพบรูปแบบการใช้งานทั่วไป:
1. ก่อนอื่นประกาศตัวแปรที่ใช้ร่วมกันเพื่อความผันผวน
2. จากนั้นใช้การอัพเดทสภาพอะตอมของ CAS เพื่อให้เกิดการซิงโครไนซ์ระหว่างเธรด
3. ในเวลาเดียวกันการสื่อสารระหว่างเธรดสามารถทำได้โดยใช้การอ่าน/เขียนของความผันผวนและความหมายหน่วยความจำของการอ่านและเขียนที่ผันผวนใน CAS
AQS, โครงสร้างข้อมูลที่ไม่ปิดกั้นและคลาสตัวแปรอะตอม (คลาสใน Java.util.concurrent.atomic Package) คลาสพื้นฐานในแพ็คเกจพร้อมกันเหล่านี้จะถูกนำมาใช้โดยใช้รูปแบบนี้และคลาสระดับสูงในแพ็คเกจพร้อมกันนั้นอาศัยคลาสพื้นฐานเหล่านี้ จากมุมมองทั่วไปแผนภาพการใช้งานของแพ็คเกจพร้อมกันมีดังนี้:
CAS (การกำหนดวัตถุในกอง):
Java เรียก new object() เพื่อสร้างวัตถุซึ่งจะถูกจัดสรรให้กับกอง JVM ดังนั้นวัตถุนี้จะบันทึกไว้ในกองได้อย่างไร?
ก่อนอื่นเมื่อมีการดำเนินการ new object() จะมีการกำหนดพื้นที่นี้มากเท่าใดเนื่องจากประเภทข้อมูลต่างๆใน Java และพื้นที่ที่พวกเขาใช้จะได้รับการแก้ไข (หากคุณไม่ชัดเจนเกี่ยวกับหลักการของมันโปรดใช้ Google ด้วยตัวคุณเอง) จากนั้นงานต่อไปคือการหาช่องว่างในกองเพื่อเก็บวัตถุนี้
ในกรณีของเธรดเดี่ยวโดยทั่วไปมีสองกลยุทธ์การจัดสรร:
1. การชนของตัวชี้: โดยทั่วไปจะใช้กับหน่วยความจำที่เป็นปกติอย่างแน่นอน (ไม่ว่าหน่วยความจำจะขึ้นอยู่กับกลยุทธ์การรีไซเคิลหน่วยความจำ) ภารกิจในการจัดสรรพื้นที่เป็นเพียงการย้ายตัวชี้เช่นระยะทางของขนาดวัตถุที่ด้านข้างของหน่วยความจำฟรี
2. รายการฟรี: เหมาะสำหรับหน่วยความจำที่ไม่ใช่ปกติ ในกรณีนี้ JVM จะรักษารายการหน่วยความจำไว้เพื่อบันทึกพื้นที่หน่วยความจำฟรีและขนาดคืออะไร เมื่อจัดสรรพื้นที่ให้กับวัตถุให้ไปที่รายการฟรีเพื่อสอบถามพื้นที่ที่เหมาะสมแล้วจัดสรร
อย่างไรก็ตามมันเป็นไปไม่ได้ที่ JVM จะทำงานในสถานะเกลียวเดียวตลอดเวลาดังนั้นประสิทธิภาพจึงแย่เกินไป เนื่องจากไม่ใช่การดำเนินการอะตอมเมื่อจัดสรรหน่วยความจำให้กับวัตถุอื่นอย่างน้อยก็จำเป็นต้องมีขั้นตอนต่อไปนี้: การค้นหารายการฟรีการจัดสรรหน่วยความจำการแก้ไขรายการฟรี ฯลฯ ซึ่งไม่ปลอดภัย นอกจากนี้ยังมีสองกลยุทธ์ในการแก้ปัญหาความปลอดภัยในระหว่างการเกิดขึ้นพร้อมกัน:
1. CAS: ในความเป็นจริงเครื่องเสมือนใช้ CAS เพื่อให้แน่ใจว่าอะตอมของการดำเนินการอัปเดตโดยไม่สามารถลองอีกครั้งและหลักการก็เหมือนกับที่กล่าวไว้ข้างต้น
2. TLAB: หากใช้ CAS จริง ๆ แล้วมันจะมีผลกระทบต่อประสิทธิภาพดังนั้น JVM จึงเสนอกลยุทธ์การเพิ่มประสิทธิภาพขั้นสูงมากขึ้น: แต่ละเธรดก่อนจัดสรรหน่วยความจำชิ้นเล็ก ๆ ในกอง Java ที่เรียกว่าบัฟเฟอร์การจัดสรรเธรดท้องถิ่น (TLAB) เมื่อเธรดจำเป็นต้องจัดสรรหน่วยความจำภายในมันก็เพียงพอที่จะจัดสรรให้โดยตรงบน TLAB หลีกเลี่ยงความขัดแย้งของเธรด เฉพาะเมื่อหน่วยความจำบัฟเฟอร์ถูกใช้งานและจำเป็นต้องจัดสรรหน่วยความจำใหม่จะดำเนินการ CAS เพื่อจัดสรรพื้นที่หน่วยความจำขนาดใหญ่ขึ้น
ไม่ว่าเครื่องเสมือนจะใช้ TLAB สามารถกำหนดค่าผ่านพารามิเตอร์ -XX:+/-UseTLAB (JDK5 และรุ่นใหม่กว่าจะเปิดใช้งานโดยค่าเริ่มต้น)
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น