ก่อนที่จะอ่านบทความนี้คุณสามารถอ่าน " บทนำและการใช้งานแพ็คเกจอะตอมมัลติเธรด Java " เพื่อเรียนรู้เกี่ยวกับเนื้อหาที่เกี่ยวข้องของแพ็คเกจอะตอม
1. อะตอมคืออะไร?
คำว่าอะตอมมีบางอย่างเกี่ยวกับอะตอมซึ่งครั้งหนึ่งเคยถูกพิจารณาว่าเป็นหน่วยของสสารที่เล็กที่สุด อะตอมในคอมพิวเตอร์หมายความว่าไม่สามารถแบ่งออกเป็นหลายส่วน หากชิ้นส่วนของรหัสได้รับการพิจารณาอะตอมหมายความว่ารหัสไม่สามารถขัดจังหวะระหว่างการดำเนินการ โดยทั่วไปแล้วคำแนะนำอะตอมจะจัดทำโดยฮาร์ดแวร์และจัดทำโดยซอฟต์แวร์เพื่อใช้วิธีการอะตอม (เธรดจะไม่ถูกขัดจังหวะหลังจากป้อนวิธีการจนกว่าการดำเนินการเสร็จสิ้น)
บนแพลตฟอร์ม x86 CPU ให้วิธีการล็อคบัสในระหว่างการดำเนินการตามคำสั่ง มี lead #hlockpin บนชิป CPU หากคำนำหน้า "ล็อค" ถูกเพิ่มลงในคำสั่งในโปรแกรมภาษาแอสเซมบลีรหัสเครื่องแอสเซมบลีจะทำให้ CPU ลดศักยภาพของ #Hlockpin เมื่อดำเนินการคำสั่งนี้และปล่อยมันจนกว่าจะสิ้นสุดคำสั่งนี้จึงล็อคบัส ด้วยวิธีนี้ซีพียูอื่น ๆ บนรถบัสเดียวกันไม่สามารถเข้าถึงหน่วยความจำผ่านบัสได้ในขณะนี้เพื่อให้มั่นใจว่าอะตอมของคำสั่งนี้ในสภาพแวดล้อมแบบมัลติโปรเซสเซอร์
2. ตัวแปรอะตอมใน java.util.concurrent
ไม่ว่าจะเป็นทางตรงหรือทางอ้อมเกือบทุกคลาสใน Java.util.concurrent Package ใช้ตัวแปรอะตอมไม่ใช่การซิงโครไนซ์ คลาสเช่น ConcurrentLinkedQueue ยังใช้ตัวแปรอะตอมเพื่อใช้อัลกอริทึมที่ไม่ต้องรอโดยตรงในขณะที่คลาสเช่น ConcurrentHashMap ใช้ reentrantlock เพื่อล็อคเมื่อจำเป็น จากนั้น reentrantlock จะใช้ตัวแปรอะตอมเพื่อรักษาคิวเธรดที่รอการล็อค
หากไม่มีการปรับปรุง JVM ใน JDK5.0 คลาสเหล่านี้จะไม่ถูกสร้างขึ้นซึ่งแสดงให้เห็น (ไปยังไลบรารีคลาสไม่ใช่คลาสผู้ใช้) อินเทอร์เฟซเพื่อเข้าถึงการซิงโครไนซ์ระดับฮาร์ดแวร์ดั้งเดิม คลาสตัวแปรอะตอมและคลาสอื่น ๆ ใน java.util.concurrent จากนั้นเปิดเผยฟังก์ชั่นเหล่านี้ไปยังคลาสผู้ใช้
คลาสอะตอมของ java.util.concurrent.atomic
แพ็คเกจนี้มีชุดคลาสอะตอม คุณลักษณะพื้นฐานของมันคือในสภาพแวดล้อมแบบมัลติเธรดเมื่อหลายเธรดดำเนินการวิธีการที่มีอยู่ในกรณีของคลาสเหล่านี้ในเวลาเดียวกันมันเป็นพิเศษนั่นคือเมื่อเธรดเข้าสู่วิธีการและดำเนินการคำแนะนำในนั้นมันจะไม่ถูกขัดจังหวะโดยเธรดอื่น ๆ และเธรดอื่น ๆ จนกว่าจะมีการดำเนินการวิธีการ JVM จะเลือกเธรดอื่นจากคิวรอเพื่อป้อน นี่เป็นเพียงความเข้าใจเชิงตรรกะ ในความเป็นจริงมันถูกนำไปใช้ด้วยความช่วยเหลือของคำแนะนำฮาร์ดแวร์ที่เกี่ยวข้องและจะไม่บล็อกเธรด (หรือเพียงแค่บล็อกที่ระดับฮาร์ดแวร์) ชั้นเรียนสามารถแบ่งออกเป็น 4 กลุ่ม
Atomicboolean, Atomicinteger, atomiclong, atomicreference
Atomicintegerarray, Atomiclongarray
Atomiclongfieldupdater, Atomicintegerfieldupdater, Atomicreferencefieldu
AtomicMarkableReference, AtomicstampedReference, AtomicreferenceArray
ในหมู่พวกเขา Atomicboolean, Atomicinteger, Atomiclong, Atomicreference มีความคล้ายคลึงกัน
ก่อนอื่น Atomicboolean, Atomicinteger, Atomiclong, APIs ภายใน Atomicreference นั้นคล้ายคลึงกัน: ยกตัวอย่างของ atomicreference
สร้างสแต็คที่ปลอดภัยจากเธรดด้วย Atomicreference
คลาสสาธารณะ LinkedStack <T> {Private AtomicReference <Node <T >> stacks = new AtomicReference <Node <t>> (); Public T Push (T E) {Node <t> oldNode, Newnode; ในขณะที่ (จริง) {// การประมวลผลที่นี่มีความพิเศษมากและต้องเป็นกรณี oldNode = stacks.get (); newNode = new node <t> (e, oldNode); if (stacks.compareandset (oldNode, newNode)) {return e;}}} สาธารณะ t pop () {nodnode <t> oldNode, newNode; (stacks.CompareAndset (oldNode, newNode)) {return oldNode.Object;}}} โหนดคลาสสุดท้ายคงที่ส่วนตัว <T> {วัตถุส่วนตัว t; โหนดส่วนตัว <t> ถัดไป; โหนดส่วนตัว (t วัตถุ, โหนด <t> ถัดไป)จากนั้นมุ่งเน้นไปที่การอัพเดทอะตอมของฟิลด์
Atomicintegerfieldupdater <T>/AtomiclongfieldUpdater <T>/AtomicReferenceFieldUpdater <T, V> เป็นค่าของสนามตามการสะท้อน
API ที่สอดคล้องกันนั้นง่ายมาก แต่ก็มีข้อ จำกัด บางอย่าง
(1) ฟิลด์จะต้องมีความผันผวนประเภท! ผันผวนคืออะไร? โปรดตรวจสอบ " คำอธิบายโดยละเอียดเกี่ยวกับคำหลักที่ผันผวนใน Java "
(2) ประเภทคำอธิบายของฟิลด์ (ตัวดัดแปลงสาธารณะ/ป้องกัน/ค่าเริ่มต้น/ส่วนตัว) สอดคล้องกับความสัมพันธ์ระหว่างผู้โทรและฟิลด์ Operation Object กล่าวคือผู้โทรสามารถใช้งานฟิลด์วัตถุโดยตรงจากนั้นสะท้อนและดำเนินการผ่าตัดอะตอม อย่างไรก็ตามสำหรับฟิลด์ของคลาสหลักไม่สามารถใช้งานคลาสย่อยได้โดยตรงแม้ว่าคลาสย่อยสามารถเข้าถึงฟิลด์ของคลาสพาเรนต์
(3) มันสามารถเป็นตัวแปรอินสแตนซ์ไม่ใช่ตัวแปรคลาสนั่นคือมันไม่สามารถเพิ่มคำหลักคงที่
(4) สามารถปรับเปลี่ยนตัวแปรเท่านั้นและไม่สามารถทำเป็นตัวแปรสุดท้ายได้เนื่องจากความหมายของขั้นสุดท้ายนั้นไม่ได้แก้ไข ในความเป็นจริงความหมายของความขัดแย้งขั้นสุดท้ายและผันผวนและคำหลักทั้งสองนี้ไม่สามารถอยู่ได้ในเวลาเดียวกัน
(5) สำหรับ AtomicintegerfieldUpdater และ AtomiclongfieldUpdater พวกเขาสามารถปรับเปลี่ยนฟิลด์ประเภท int/ยาวเท่านั้นและไม่สามารถปรับเปลี่ยนประเภท wrapper ของพวกเขา (จำนวนเต็ม/ยาว) หากคุณต้องการแก้ไขประเภทการห่อคุณต้องใช้ AtomicReferenceFieldFieldUpdater
วิธีการดำเนินการอธิบายไว้ในตัวอย่างต่อไปนี้
นำเข้า java.util.concurrent.atomic.atomicintegerfieldupdater; คลาสสาธารณะ Atomicintegerfieldupdaterdemo {Class Demodata {Valatile Int Value 1 = 1; ค่า intcolatile 2 = 2; FieldName) {return atomicintegerfieldupdater.newupdater (demodata.class, fieldname);} void doit () {demodata data = demodata ใหม่ (); system.out.println ("1 ==>"+getupdater ("value1") "+getUpdater (" value2 "). เพิ่มขึ้น (ข้อมูล)); system.out.println (" 2 ==> "+getUpdater (" value3 "). decrementandget (data)); system.out.println (" true ==> "getUpDater (" value4 ") {Atomicintegerfieldupdaterdemo demo = ใหม่ Atomicintegerfieldupdaterdemo (); demo.doit ();}}ในตัวอย่างข้างต้นค่าฟิลด์ค่า 3/ค่า 4 ของ demodata ไม่สามารถมองเห็นได้ในคลาส Atomicintegerfieldupdaterdemo ดังนั้นค่าของมันจึงไม่สามารถแก้ไขได้โดยตรงผ่านการสะท้อนกลับ
คู่ของ <วัตถุบูลีน> อธิบายโดยคลาส AtommarkableReference สามารถปรับเปลี่ยนค่าของวัตถุหรือบูลีนได้ โครงสร้างข้อมูลนี้มีประโยชน์มากขึ้นในคำอธิบายแคชหรือสถานะบางอย่าง โครงสร้างนี้สามารถปรับปรุงปริมาณงานได้อย่างมีประสิทธิภาพเมื่อแก้ไขวัตถุ/บูลีนเป็นรายบุคคลหรือพร้อมกัน
คลาส AtomicstampedReference จะรักษาการอ้างอิงวัตถุด้วย "ธง" จำนวนเต็มและสามารถอัปเดตอะตอมได้ เมื่อเปรียบเทียบกับ <วัตถุบูลีน> ของคลาส AtommarkableReference, AtomicStampedReference ยังคงรักษาโครงสร้างข้อมูลที่คล้ายกับ <Object, int> ซึ่งจริง ๆ แล้วเป็นจำนวนวัตถุที่เกิดขึ้นพร้อมกัน (การอ้างอิง) แต่แตกต่างจาก Atomicinteger โครงสร้างข้อมูลนี้สามารถดำเนินการอ้างอิงวัตถุและสามารถดำเนินการเกี่ยวกับอะตอมในวัตถุนี้และนับในเวลาเดียวกัน
"ปัญหา ABA" จะถูกกล่าวถึงในตอนท้ายของบทความนี้และ AtomicMarkableReference/AtomicstampedReference มีประโยชน์ในการแก้ปัญหา "ปัญหา ABA"
iii. บทบาทของอะตอม
สิ่งนี้ช่วยให้การทำงานของข้อมูลเดียวเป็นอะตอม
สร้างรหัสที่ซับซ้อนและปิดกั้นโดยใช้คลาสอะตอม
การเข้าถึงตัวแปรอะตอม 2 ตัวขึ้นไป (หรือดำเนินการ 2 ครั้งขึ้นไปในตัวแปรอะตอมเดียว) โดยทั่วไปจะพิจารณาว่าต้องมีการซิงโครไนซ์เพื่อให้การดำเนินการเหล่านี้สามารถใช้เป็นหน่วยอะตอม
ไม่มีการล็อคและไม่มีอัลกอริทึมรอ
อัลกอริธึมพร้อมกันที่ใช้ CAS (เปรียบเทียบและ SWAP) เรียกว่าอัลกอริทึมฟรีล็อคเนื่องจากเธรดไม่ต้องรอการล็อคอีกต่อไป (บางครั้งเรียกว่า mutexes หรือชิ้นส่วนที่สำคัญขึ้นอยู่กับคำศัพท์ของแพลตฟอร์มเธรด) ไม่ว่าการดำเนินการ CAS จะประสบความสำเร็จหรือล้มเหลวไม่ว่าในกรณีใดก็ตามจะเสร็จสิ้นภายในเวลาที่คาดการณ์ได้ หาก CAS ล้มเหลวผู้โทรสามารถลองดำเนินการ CAS หรือดำเนินการอื่น ๆ ที่เหมาะสมอื่น ๆ
หากแต่ละเธรดยังคงทำงานอย่างต่อเนื่องเมื่อเธรดอื่นล่าช้า (หรือล้มเหลว) อาจกล่าวได้ว่าอัลกอริทึมรออยู่ฟรี ในทางตรงกันข้ามอัลกอริทึมที่ปราศจากล็อคต้องการเพียงเธรดบางอย่างเท่านั้นที่จะทำการดำเนินการเสมอ (คำจำกัดความอื่นของการไม่รอคือเพื่อให้แน่ใจว่าแต่ละเธรดจะคำนวณการดำเนินการของตัวเองอย่างถูกต้องในขั้นตอนที่ จำกัด โดยไม่คำนึงถึงการดำเนินการเวลาครอสโอเวอร์หรือความเร็วของเธรดอื่น ๆ ขีด จำกัด นี้อาจเป็นฟังก์ชันของจำนวนเธรดในระบบตัวอย่างเช่นหากมีอีก 10 เธรด
ในช่วง 15 ปีที่ผ่านมาผู้คนได้ทำการวิจัยอย่างกว้างขวางเกี่ยวกับอัลกอริทึมที่ปราศจากการรอคอยและปราศจากล็อค (หรือที่เรียกว่าอัลกอริทึมที่ไม่ปิดกั้น) และหลายคนได้ค้นพบอัลกอริทึมที่ไม่ปิดกั้นในโครงสร้างข้อมูลทั่วไป อัลกอริทึมที่ไม่ปิดกั้นถูกนำมาใช้อย่างกว้างขวางในระบบปฏิบัติการและระดับ JVM การทำงานเช่นการทำเกลียวและการกำหนดเวลากระบวนการ แม้ว่าการใช้งานของพวกเขาจะซับซ้อนมากขึ้น แต่พวกเขามีข้อได้เปรียบมากมายจากอัลกอริธึมทางเลือกที่ใช้ล็อค: พวกเขาสามารถหลีกเลี่ยงอันตรายเช่นการผกผันลำดับความสำคัญและการหยุดชะงักการแข่งขันมีราคาถูกกว่าการประสานงานเกิดขึ้นในระดับความละเอียดที่ดีขึ้น
ทั่วไป:
เคาน์เตอร์ที่ไม่ปิดกั้น
สแต็คที่ไม่ปิดกั้นพร้อมกัน
รายการที่เชื่อมโยงไม่ปิดกั้นพร้อมกัน
คำถาม ABA:
เพราะก่อนที่จะเปลี่ยน V CAS ส่วนใหญ่ถามว่า "ค่าของ V ยังคงเป็น" ก่อนที่จะอ่าน V เป็นครั้งแรกและดำเนินการ CAS ใน V หรือไม่การเปลี่ยนค่าจาก A เป็น B และกลับไปที่ A จะทำให้อัลกอริทึมที่ใช้ CAS สับสน ในกรณีนี้การดำเนินการ CAS จะประสบความสำเร็จ แต่ในบางกรณีผลลัพธ์อาจไม่ใช่สิ่งที่คุณคาดหวัง ปัญหาประเภทนี้เรียกว่าปัญหา ABA ซึ่งมักจะจัดการโดยการเชื่อมโยงหมายเลขแท็กหรือเวอร์ชันกับแต่ละค่าที่จะดำเนินการในการดำเนินการ CAS และอัพเดทค่าอะตอมและแท็ก คลาส AtomicstampedReference รองรับวิธีนี้
สรุป
ข้างต้นเป็นคำอธิบายโดยละเอียดทั้งหมดของบทความนี้เกี่ยวกับการใช้งานตัวแปรอะตอมและคลาสอะตอมในแพ็คเกจอะตอมมัลติเธรด Java ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน เพื่อนที่สนใจสามารถอ้างถึงเว็บไซต์นี้ต่อไปได้:
การเขียนโปรแกรม Java: การหยุดชะงักแบบมัลติเธรดและการสื่อสารระหว่างเธรดเป็นรหัสง่ายๆ
Java Multi-Threaded Programming ตัวอย่างขนาดเล็กจำลองระบบลานจอดรถ
การอภิปรายสั้น ๆ เกี่ยวกับข้อดีและตัวอย่างรหัสของ Java multithreading
หากมีข้อบกพร่องใด ๆ โปรดฝากข้อความไว้เพื่อชี้ให้เห็น