การปรับเปลี่ยนของCollection
ในขณะที่ iterating ผ่านที่Collection
ใช้Iterator
จะไม่ได้รับอนุญาตโดยส่วนใหญ่ของCollection
ชั้นเรียน ไลบรารี Java เรียกความพยายามที่จะแก้ไขไฟล์Collection
ในขณะที่ทำซ้ำผ่านมันว่า "การแก้ไขพร้อมกัน" น่าเสียดายที่ชี้ให้เห็นว่าสาเหตุที่เป็นไปได้เพียงอย่างเดียวคือการแก้ไขพร้อมกันหลายเธรด แต่ไม่เป็นเช่นนั้น การใช้เธรดเดียวเป็นไปได้ที่จะสร้างตัววนซ้ำสำหรับCollection
(การใช้Collection.iterator()
หรือการวนซ้ำขั้นสูงfor
) เริ่มการวนซ้ำ (โดยใช้Iterator.next()
หรือเข้าสู่เนื้อหาของfor
ลูปที่ปรับปรุงแล้ว) แก้ไขCollection
จากนั้นทำซ้ำต่อไป
เพื่อช่วยโปรแกรมเมอร์การนำไปใช้งานบางอย่างของCollection
คลาสเหล่านั้นพยายามตรวจจับการปรับเปลี่ยนพร้อมกันที่ผิดพลาดและทำให้เกิดข้อผิดพลาดConcurrentModificationException
หากตรวจพบ อย่างไรก็ตามโดยทั่วไปไม่สามารถทำได้และเป็นประโยชน์ในการรับประกันการตรวจจับการแก้ไขพร้อมกันทั้งหมด การใช้งานที่ผิดพลาดจึงCollection
ไม่ส่งผลให้เกิดการโยนทิ้งConcurrentModificationException
เสมอไป
เอกสารConcurrentModificationException
ระบุว่า:
ข้อยกเว้นนี้อาจเกิดขึ้นโดยวิธีการที่ตรวจพบการปรับเปลี่ยนวัตถุในเวลาเดียวกันเมื่อไม่อนุญาตให้มีการปรับเปลี่ยนดังกล่าว ...
โปรดทราบว่าข้อยกเว้นนี้ไม่ได้ระบุเสมอว่าอ็อบเจ็กต์ถูกแก้ไขพร้อมกันโดยเธรดอื่น หากเธรดเดียวออกลำดับของการเรียกใช้เมธอดที่ละเมิดสัญญาของอ็อบเจ็กต์อ็อบเจ็กต์อาจโยนข้อยกเว้นนี้ ...
โปรดทราบว่าไม่สามารถรับประกันพฤติกรรมที่ล้มเหลวได้อย่างที่เป็นอยู่โดยทั่วไปแล้วเป็นไปไม่ได้ที่จะทำการรับประกันอย่างหนักหน่วงใด ๆ เมื่อมีการปรับเปลี่ยนพร้อมกันที่ไม่ซิงโครไนซ์ การดำเนินการที่ล้มเหลวอย่างรวดเร็วจะConcurrentModificationException
ใช้ความพยายามอย่างเต็มที่
โปรดทราบว่า
เอกสารประกอบของHashSet
, HashMap
, TreeSet
และArrayList
ชั้นเรียนบอกว่านี่:
ตัวทำซ้ำที่ส่งคืน [โดยตรงหรือโดยอ้อมจากคลาสนี้] จะล้มเหลวอย่างรวดเร็ว: ถ้า [คอลเลกชัน] ถูกแก้ไขเมื่อใดก็ได้หลังจากสร้างตัววนซ้ำไม่ว่าจะด้วยวิธีใดก็ตามยกเว้นผ่านวิธีการลบของตัววนซ้ำผู้ทำจะIterator
พ่น a ConcurrentModificationException
. ดังนั้นเมื่อเผชิญกับการปรับเปลี่ยนพร้อมกันตัววนซ้ำจะล้มเหลวอย่างรวดเร็วและหมดจดแทนที่จะเสี่ยงต่อพฤติกรรมที่ไม่ได้กำหนดโดยพลการในเวลาที่ไม่กำหนดในอนาคต
โปรดทราบว่าไม่สามารถรับประกันลักษณะการทำงานที่ล้มเหลวอย่างรวดเร็วของตัววนซ้ำได้ตามที่เป็นอยู่โดยทั่วไปแล้วจะไม่สามารถรับประกันได้ยากใด ๆ เมื่อมีการแก้ไขพร้อมกันที่ไม่ซิงโครไนซ์ ตัวทำซ้ำที่รวดเร็วล้มเหลวจะConcurrentModificationException
ใช้ความพยายามอย่างเต็มที่ ดังนั้นจึงเป็นเรื่องผิดที่จะเขียนโปรแกรมที่ขึ้นอยู่กับข้อยกเว้นนี้เพื่อความถูกต้อง: ควรใช้ลักษณะการทำงานที่ล้มเหลวอย่างรวดเร็วของตัวทำซ้ำเพื่อตรวจจับข้อบกพร่องเท่านั้น
โปรดทราบอีกครั้งว่าพฤติกรรม "ไม่สามารถรับประกันได้" และเป็นเพียง "ตามความพยายามอย่างเต็มที่"
เอกสารของวิธีการต่างๆของMap
อินเทอร์เฟซระบุว่า:
การใช้งานที่ไม่เกิดขึ้นพร้อมกันควรแทนที่วิธีการนี้และConcurrentModificationException
หากตรวจพบว่าฟังก์ชันการทำแผนที่แก้ไขแผนที่นี้ในระหว่างการคำนวณโดยใช้ความพยายามอย่างเต็มที่ การใช้งานพร้อมกันควรแทนที่วิธีการนี้และIllegalStateException
หากตรวจพบว่าฟังก์ชันการทำแผนที่จะแก้ไขแผนที่นี้ในระหว่างการคำนวณและการคำนวณผลลัพธ์จะไม่เสร็จสมบูรณ์
โปรดทราบอีกครั้งว่าจำเป็นต้องมี "พื้นฐานความพยายามอย่างดีที่สุด" เท่านั้นสำหรับการตรวจจับและConcurrentModificationException
แนะนำอย่างชัดเจนสำหรับคลาสที่ไม่พร้อมกันเท่านั้น (ไม่ใช่เธรด - ปลอดภัย)
แก้จุดบกพร่อง ConcurrentModificationException
ดังนั้นเมื่อคุณเห็นสแต็กแทร็กเนื่องจาก a ConcurrentModificationException
คุณไม่สามารถสันนิษฐานได้ทันทีว่าสาเหตุคือการเข้าถึงแบบมัลติเธรดที่ไม่ปลอดภัยไปยังไฟล์Collection
. คุณต้องตรวจสอบการติดตามสแต็กเพื่อพิจารณาว่าคลาสของการCollection
โยนข้อยกเว้น (เมธอดของคลาสจะโยนมันโดยตรงหรือโดยอ้อม) และสำหรับCollection
อ็อบเจ็กต์ใด จากนั้นคุณต้องตรวจสอบว่าสามารถแก้ไขวัตถุนั้นได้จากที่ใด
- สาเหตุที่พบบ่อยที่สุดคือการแก้ไข
Collection
ภายในfor
ลูปที่ปรับปรุงแล้วบนCollection
. เพียงเพราะคุณไม่เห็นIterator
วัตถุในซอร์สโค้ดของคุณไม่ได้หมายความว่าไม่มีIterator
! โชคดีที่หนึ่งในข้อความของfor
ลูปที่ผิดปกติมักจะอยู่ในสแต็กแทร็กดังนั้นการติดตามข้อผิดพลาดจึงมักทำได้ง่าย
- กรณีที่ยากกว่าคือเมื่อรหัสของคุณส่งผ่านการอ้างอิงไปยัง
Collection
วัตถุ โปรดทราบว่ามุมมองที่ไม่สามารถแก้ไขได้ของคอลเลกชัน (เช่นผลิตโดยCollections.unmodifiableList()
) ยังคงมีการอ้างอิงถึงคอลเล็กชันที่ปรับเปลี่ยนได้ดังนั้นการทำซ้ำในคอลเล็กชันที่ "ไม่สามารถแก้ไขได้" อาจทำให้เกิดข้อยกเว้นได้ (การแก้ไขได้ดำเนินการที่อื่นแล้ว) อื่น ๆมุมมองของคุณCollection
เช่นรายการย่อย , Map
ชุดรายการและMap
ชุดที่สำคัญยังรักษาอ้างอิงไปที่เดิม Collection
(แก้ไขได้) ซึ่งอาจเป็นปัญหาได้แม้สำหรับเธรดที่ปลอดภัยCollection
เช่นCopyOnWriteList
; อย่าถือว่าคอลเลกชันที่ปลอดภัยต่อเธรด (พร้อมกัน) ไม่สามารถส่งข้อยกเว้นได้
- การดำเนินการใดที่สามารถแก้ไขข้อผิดพลาด
Collection
ได้ในบางกรณี ยกตัวอย่างเช่นการปรับเปลี่ยนคอลเลกชันLinkedHashMap.get()
- กรณีที่ยากที่สุดเมื่อข้อยกเว้นคือเนื่องจากการปรับเปลี่ยนพร้อมกันโดยหลายหัวข้อ
การเขียนโปรแกรมเพื่อป้องกันข้อผิดพลาดในการแก้ไขพร้อมกัน
หากเป็นไปได้ให้ จำกัด การอ้างอิงทั้งหมดไปยังCollection
อ็อบเจ็กต์เพื่อป้องกันการแก้ไขพร้อมกันได้ง่ายขึ้น ทำให้วัตถุหรือตัวแปรท้องถิ่นและไม่กลับอ้างอิงถึงหรือ iterators จากวิธีการ จากนั้นจะง่ายกว่ามากในการตรวจสอบสถานที่ทั้งหมดที่สามารถแก้ไขได้ หากจะใช้กับเธรดหลายเธรดจะเป็นการปฏิบัติเพื่อให้แน่ใจว่าเธรดเข้าถึงเฉพาะด้วยการซิงโครไนซ์และการล็อกที่เหมาะสมCollection
private
Collection
Collection
Collection
Collection