นี่คือคำอธิบายและตัวอย่างของวิธีการที่จะประสบความสำเร็จ แจ้งให้เราทราบหากมีชิ้นส่วนที่ไม่ชัดเจน
สรุปสาระสำคัญด้วยแหล่งที่มา
สากล
การเริ่มต้น:
ดัชนีเธรดถูกนำไปใช้ในแบบที่เพิ่มขึ้นแบบอะตอม นี้จัดการโดยใช้ชื่อAtomicInteger
nextIndex
ดัชนีเหล่านี้ถูกกำหนดให้กับเธรดผ่านThreadLocal
อินสแตนซ์ซึ่งเริ่มต้นตัวเองโดยรับดัชนีถัดไปจากnextIndex
และเพิ่มขึ้น สิ่งนี้จะเกิดขึ้นในครั้งแรกที่มีการดึงดัชนีของแต่ละเธรดในครั้งแรก A ThreadLocal
ถูกสร้างขึ้นเพื่อติดตามลำดับสุดท้ายที่กระทู้นี้สร้างขึ้น มันเริ่มต้นได้ 0 การอ้างอิงวัตถุจากโรงงานตามลำดับจะถูกส่งผ่านและเก็บไว้ สองกรณีจะถูกสร้างขึ้นขนาดAtomicReferenceArray
n
วัตถุหางถูกกำหนดให้กับการอ้างอิงแต่ละรายการซึ่งได้รับการกำหนดค่าเริ่มต้นด้วยสถานะเริ่มต้นจากSequential
โรงงาน n
เป็นจำนวนเธรดสูงสุดที่อนุญาต แต่ละองค์ประกอบในอาร์เรย์เหล่านี้ 'อยู่' กับดัชนีเธรดที่สอดคล้องกัน
ใช้วิธี:
นี่คือวิธีการทำงานที่น่าสนใจ มันทำต่อไปนี้:
- สร้างโหนดใหม่สำหรับการเรียกใช้นี้: mine
- ตั้งค่าโหนดใหม่นี้ในอาร์เรย์ประกาศที่ดัชนีของเธรดปัจจุบัน
จากนั้นจะเริ่มการวนซ้ำลำดับ มันจะดำเนินต่อไปจนกว่าการร้องขอปัจจุบันจะถูกจัดลำดับ:
- ค้นหาโหนดในอาร์เรย์ประกาศโดยใช้ลำดับของโหนดสุดท้ายที่สร้างโดยเธรดนี้ เพิ่มเติมเกี่ยวกับเรื่องนี้ในภายหลัง
- หากพบโหนดในขั้นตอนที่ 2 แต่ยังไม่ได้เรียงลำดับให้ดำเนินการต่อไปมิเช่นนั้นก็เพียงเน้นที่การเรียกใช้ปัจจุบัน สิ่งนี้จะพยายามช่วยโหนดอื่น ๆ หนึ่งโหนดต่อการเรียกใช้
- ไม่ว่าจะเลือกโหนดใดในขั้นตอนที่ 3 พยายามลำดับต่อไปหลังจากโหนดลำดับสุดท้าย (เธรดอื่นอาจรบกวน) โดยไม่คำนึงถึงความสำเร็จตั้งค่าการอ้างอิงส่วนหัวของเธรดปัจจุบันเป็นลำดับที่ส่งกลับโดย
decideNext()
กุญแจสำคัญในการวนซ้ำซ้อนที่อธิบายไว้ข้างต้นเป็นdecideNext()
วิธีการ เพื่อให้เข้าใจว่าเราต้องดูคลาสของโหนด
คลาสโหนด
คลาสนี้ระบุโหนดในรายการที่ลิงก์เป็นสองเท่า การกระทำในคลาสนี้มีไม่มากนัก วิธีการส่วนใหญ่เป็นวิธีการค้นคืนแบบง่าย ๆ ซึ่งควรอธิบายได้ด้วยตนเอง
วิธีหาง
สิ่งนี้จะคืนค่าอินสแตนซ์โหนดพิเศษที่มีลำดับ 0 มันจะทำหน้าที่เป็นตัวยึดตำแหน่งจนกว่าการร้องขอจะแทนที่มัน
คุณสมบัติและการเริ่มต้น
seq
: หมายเลขลำดับ, เริ่มต้นที่ -1 (หมายถึงไม่มีลำดับ)
invocation
: apply()
ค่าของการภาวนาของ ตั้งอยู่บนการก่อสร้าง
next
: AtomicReference
สำหรับลิงค์ไปข้างหน้า เมื่อกำหนดแล้วจะไม่มีการเปลี่ยนแปลง
previous
: AtomicReference
สำหรับลิงก์ย้อนกลับที่กำหนดตามลำดับและลบโดยtruncate()
ตัดสินใจต่อไป
วิธีนี้เป็นเพียงหนึ่งเดียวในโหนดที่มีตรรกะที่ไม่น่ารำคาญ โดยสรุปแล้วโหนดจะเสนอเป็นตัวเลือกให้เป็นโหนดถัดไปในรายการที่เชื่อมโยง compareAndSet()
วิธีการจะตรวจสอบหากมีการอ้างอิงเป็นโมฆะและถ้าเป็นเช่นนั้นการตั้งค่าการอ้างอิงไปยังผู้สมัคร หากการอ้างอิงถูกตั้งค่าไว้แล้วจะไม่ทำอะไรเลย การดำเนินการนี้เป็นแบบอะตอมดังนั้นหากมีผู้สมัครสองคนในเวลาเดียวกันจะมีเพียงคนเดียวเท่านั้นที่เลือก สิ่งนี้รับประกันเพียงหนึ่งโหนดเท่านั้นที่จะถูกเลือกเป็นโหนดถัดไป หากเลือกโหนดตัวเลือกลำดับของมันจะถูกตั้งค่าเป็นค่าถัดไปและเป็นลิงค์ก่อนหน้าของมันถูกตั้งค่าเป็นโหนดนี้
การกระโดดกลับไปที่คลาส Universal ใช้วิธีการ ...
การเรียกdecideNext()
ใช้โหนดลำดับต่อไปล่าสุด (เมื่อตรวจสอบ) กับโหนดของเราหรือโหนดจากannounce
อาเรย์มีสองเหตุการณ์ที่เป็นไปได้: 1. โหนดลำดับที่ประสบความสำเร็จ 2. บางกระทู้อื่น ๆ pre-empted เธรดนี้
ขั้นตอนต่อไปคือการตรวจสอบว่าโหนดที่สร้างขึ้นสำหรับการร้องขอนี้ สิ่งนี้อาจเกิดขึ้นได้เนื่องจากเธรดนี้ประสบความสำเร็จในการจัดลำดับเธรดหรือบางเธรดอื่นหยิบมันขึ้นมาจากannounce
อาร์เรย์และจัดลำดับให้เรา หากยังไม่ได้รับการจัดลำดับกระบวนการจะทำซ้ำ มิฉะนั้นการโทรจะเสร็จสิ้นโดยการล้างอาร์เรย์ประกาศสำหรับที่ดัชนีของเธรดนี้และส่งคืนค่าผลลัพธ์ของการเรียกใช้ อาร์เรย์การประกาศจะถูกเคลียร์เพื่อรับประกันว่าไม่มีการอ้างอิงถึงโหนดที่เหลืออยู่รอบ ๆ ซึ่งจะป้องกันไม่ให้โหนดถูกรวบรวมขยะและดังนั้นจึงเก็บโหนดทั้งหมดในรายการที่ลิงก์จากจุดนั้นบนชีวิตบนฮีป
ประเมินวิธีการ
ในตอนนี้โหนดของการเรียกใช้ได้รับการจัดลำดับเรียบร้อยแล้วจำเป็นต้องประเมินการเรียกใช้ ในการทำเช่นนั้นขั้นตอนแรกคือการตรวจสอบให้แน่ใจว่ามีการประเมินการเรียกใช้ก่อนหน้านี้ หากพวกเขาไม่มีเธรดนี้จะไม่รอ แต่จะทำงานทันที
วิธีการ VerifyPrior
ensurePrior()
วิธีการทำงานนี้โดยการตรวจสอบโหนดก่อนหน้านี้ในรายการที่เชื่อมโยง หากไม่ได้ตั้งค่าสถานะจะมีการประเมินโหนดก่อนหน้า โหนดที่เป็นแบบเรียกซ้ำ หากโหนดก่อนหน้านี้โหนดก่อนหน้านี้ไม่ได้รับการประเมินก็จะเรียกการประเมินผลสำหรับโหนดนั้นและอื่น ๆ
ขณะนี้ทราบว่าโหนดก่อนหน้านี้มีสถานะเราสามารถประเมินโหนดนี้ได้ โหนดสุดท้ายถูกดึงและกำหนดให้กับตัวแปรโลคัล หากการอ้างอิงนี้เป็นโมฆะก็หมายความว่าเธรดอื่น ๆ ได้ทำการจองข้อมูลนี้ล่วงหน้าแล้วและประเมินโหนดนี้แล้ว การตั้งค่าเป็นรัฐ มิฉะนั้นสถานะของโหนดก่อนหน้านี้จะถูกส่งผ่านไปยังSequential
วิธีการใช้ของวัตถุพร้อมกับการร้องขอของโหนดนี้ สถานะที่ส่งคืนถูกตั้งค่าไว้บนโหนดและtruncate()
วิธีการนั้นเรียกว่าเป็นการล้างลิงก์ย้อนกลับจากโหนดเนื่องจากไม่ต้องการอีกต่อไป
วิธีการ MoveForward
วิธีการย้ายไปข้างหน้าจะพยายามย้ายการอ้างอิงส่วนหัวทั้งหมดไปยังโหนดนี้หากพวกเขาไม่ได้ชี้ไปที่บางสิ่งเพิ่มเติมตาม นี่คือเพื่อให้แน่ใจว่าหากเธรดหยุดการโทรส่วนหัวของมันจะไม่เก็บการอ้างอิงไปยังโหนดที่ไม่ต้องการอีกต่อไป compareAndSet()
วิธีการที่จะทำให้แน่ใจว่าเราจะอัปเดตโหนดถ้าบางหัวข้ออื่น ๆ ยังไม่ได้เปลี่ยนมันตั้งแต่มันถูกดึง
ประกาศอาร์เรย์และช่วยเหลือ
กุญแจสำคัญในการทำให้วิธีการนี้ปราศจากการรอซึ่งตรงข้ามกับการล็อคฟรีก็คือเราไม่สามารถสันนิษฐานได้ว่าตัวจัดกำหนดการเธรดจะให้ความสำคัญกับเธรดแต่ละรายการเมื่อจำเป็น หากแต่ละเธรดพยายามเรียงลำดับโหนดของตนเองเป็นไปได้ที่เธรดอาจถูกจองไว้ล่วงหน้าอย่างต่อเนื่องภายใต้โหลด ในการพิจารณาถึงความเป็นไปได้นี้แต่ละเธรดจะพยายาม 'ช่วย' เธรดอื่น ๆ ที่อาจไม่สามารถเรียงลำดับได้
แนวคิดพื้นฐานคือเมื่อแต่ละเธรดสร้างโหนดได้สำเร็จลำดับที่ได้รับจะเพิ่มขึ้นอย่างน่าเบื่อ หากเธรดหรือเธรดถูกจองล่วงหน้าเธรดอื่นอย่างต่อเนื่องดัชนีการใช้เพื่อค้นหาโหนดที่ตามมาในannounce
อาร์เรย์จะเลื่อนไปข้างหน้า แม้ว่าเธรดทุกอันที่พยายามเรียงลำดับโหนดที่ให้นั้นจะถูกจองไว้ล่วงหน้าอย่างต่อเนื่องโดยเธรดอื่นในที่สุดเธรดทั้งหมดจะพยายามเรียงลำดับโหนดนั้น เพื่อแสดงให้เห็นว่าเราจะสร้างตัวอย่างที่มีสามกระทู้
ที่จุดเริ่มต้นองค์ประกอบทั้งสามของเธรดและประกาศจะถูกชี้ไปที่tail
โหนด lastSequence
สำหรับแต่ละหัวข้อคือ 0
ณ จุดนี้เธรด 1ถูกเรียกใช้งานด้วยการเรียกใช้ มันตรวจสอบอาร์เรย์ประกาศว่ามันเป็นลำดับสุดท้าย (ศูนย์) ซึ่งเป็นโหนดที่มีการจัดตารางเวลาให้ดัชนี มันเรียงลำดับโหนดและมันlastSequence
ถูกตั้งค่าเป็น 1
ตอนนี้เธรด 2จะถูกดำเนินการด้วยการเรียกใช้มันจะตรวจสอบอาร์เรย์การประกาศที่ลำดับสุดท้าย (ศูนย์) และเห็นว่าไม่ต้องการความช่วยเหลือและพยายามเรียงลำดับการเรียกใช้ มันประสบความสำเร็จและตอนนี้มันlastSequence
ถูกตั้งค่าเป็น 2
ตอนนี้เธรด 3ถูกดำเนินการแล้วและยังเห็นว่าโหนดที่announce[0]
ได้รับการจัดลำดับแล้วและมีการจัดลำดับเป็นของตนเอง มันlastSequence
เป็นชุดนี้ถึง 3
ตอนนี้เธรด 1ถูกเรียกอีกครั้ง มันตรวจสอบอาร์เรย์ประกาศที่ดัชนี 1 และพบว่ามันถูกจัดลำดับแล้ว ในขณะเดียวกันเธรด 2จะถูกเรียกใช้ มันตรวจสอบประกาศอาร์เรย์ที่ดัชนี 2 และพบว่ามันมีการเรียงลำดับแล้ว ตอนนี้ทั้งเธรด 1และเธรด 2พยายามเรียงลำดับโหนดของตนเอง หัวข้อที่ 2ชนะและมันเรียงตามลำดับการเรียก มันlastSequence
ถูกตั้งค่าเป็น 4 ในขณะเดียวกันเธรดที่สามถูกเรียกใช้ มันตรวจสอบดัชนีมันlastSequence
(mod 3) และพบว่าโหนดที่announce[0]
ไม่ได้ถูกจัดลำดับ เธรด 2จะถูกเรียกใช้อีกครั้งในเวลาเดียวกันกับที่เธรด 1กำลังดำเนินอยู่เป็นครั้งที่สอง ด้าย 1พบว่าการภาวนา unsequenced ที่announce[1]
ซึ่งเป็นโหนดเพิ่งสร้างขึ้นโดยกระทู้ 2 พยายามเรียงลำดับการเรียกใช้เธรด 2และทำสำเร็จ เธรด 2พบว่าเป็นโหนดของตนเองannounce[1]
และได้รับการจัดลำดับแล้ว มันตั้งค่าเป็นlastSequence
5 เธรด 3จะถูกเรียกใช้และพบว่าโหนดที่เธรด 1 ที่วางที่announce[0]
ยังไม่ได้ถูกจัดลำดับและพยายามทำเช่นนั้น ในขณะเดียวกันก็มีการเรียกใช้เธรด 2และทำให้เธรด 3 ว่างไว้ล่วงหน้าซึ่งจะเรียงลำดับโหนดและตั้งค่าเป็นlastSequence
6
ด้ายไม่ดี1 . แม้ว่าเธรด 3พยายามเรียงลำดับเธรดทั้งสองจะถูกขัดขวางอย่างต่อเนื่องโดยตัวกำหนดตารางเวลา แต่ ณ จุดนี้ ตอนนี้เธรด 2ยังชี้ไปที่announce[0]
(6 mod 3) ทั้งสามกระทู้ถูกตั้งค่าให้พยายามเรียงลำดับการเรียกใช้เดียวกัน เรื่องที่ประสบความสำเร็จด้ายโหนดถัดไปที่จะติดใจจะภาวนารอกระทู้ 1announce[0]
คือโหนดอ้างอิงโดย
นี่เป็นสิ่งที่หลีกเลี่ยงไม่ได้ เพื่อให้เธรดถูกจองล่วงหน้าเธรดอื่นจะต้องเรียงลำดับโหนดและเมื่อทำเช่นนั้นเธรดเหล่านั้นจะเลื่อนlastSequence
ไปข้างหน้าอย่างต่อเนื่อง หากโหนดของเธรดที่ระบุไม่ได้ถูกจัดลำดับอย่างต่อเนื่องในที่สุดเธรดทั้งหมดจะชี้ไปที่ดัชนีของมันในอาร์เรย์ประกาศ เธรดจะไม่ทำสิ่งใดจนกว่าโหนดที่พยายามจะช่วยได้รับการจัดลำดับสถานการณ์กรณีที่เลวร้ายที่สุดคือเธรดทั้งหมดจะชี้ไปยังโหนดที่ตามมาแบบเดียวกัน ดังนั้นเวลาที่ต้องใช้ในการจัดลำดับการเรียกใช้ใด ๆ เป็นฟังก์ชันของจำนวนเธรดไม่ใช่ขนาดของอินพุต