ผู้เขียนบันทึกนี้คือ Grzegorz Mirek ผู้พัฒนาซอฟต์แวร์จากคราคูฟ (โปแลนด์) เขาเริ่มพัฒนาทักษะในชวาเมื่อประมาณ 6 ปีที่แล้ว ขณะที่ยังเรียนอยู่ในมหาวิทยาลัย และตั้งแต่นั้นเป็นต้นมา เขาได้ขัดเกลาทักษะในด้านนี้อย่างไม่รู้จักเหน็ดเหนื่อย เขาสนใจเป็นพิเศษในเรื่องประสิทธิภาพและการเพิ่ม ประสิทธิภาพของ JVM ซึ่งเป็นสิ่งที่เขาเขียนเกี่ยวกับในบล็อก ของเขาเป็นหลัก
คำถามสัมภาษณ์ Java ที่ได้รับความนิยมมากที่สุดบางข้อ ได้แก่: อะไรคือความแตกต่างระหว่างตัววนซ้ำแบบรวดเร็วและไม่ปลอดภัย? คำตอบที่ง่ายที่สุดสำหรับสิ่งนี้คือ: ตัววนซ้ำที่เร็วเมื่อล้มเหลวจะส่ง ConcurrentModificationException หากคอลเลกชันเปลี่ยนแปลงในระหว่างการวนซ้ำ แต่ตัววนซ้ำที่ไม่ปลอดภัยจะไม่ทำ แม้ว่าสิ่งนี้จะดูมีความหมาย แต่ก็ยังไม่มีความชัดเจนว่าผู้สัมภาษณ์หมายถึงอะไรในเรื่องความปลอดภัยจากความล้มเหลว ข้อมูลจำเพาะภาษา Java ไม่ได้กำหนดคำนี้ที่เกี่ยวข้องกับตัววนซ้ำ อย่างไรก็ตาม มีกลยุทธ์การปรับเปลี่ยนเพื่อการแข่งขันอยู่สี่กลยุทธ์
การปรับเปลี่ยนการแข่งขัน
ขั้นแรก เรามากำหนดว่าการปรับเปลี่ยนแบบแข่งขัน (หรือแบบขนาน) คืออะไร สมมติว่าเรามีคอลเลกชันและเมื่อตัววนซ้ำทำงานอยู่ การเปลี่ยนแปลงบางอย่างเกิดขึ้นโดยไม่ได้มาจากตัววนซ้ำนี้ ในกรณีนี้ เราได้รับการปรับเปลี่ยนที่แข่งขันได้ ผมขอยกตัวอย่างง่ายๆ ให้กับคุณ: สมมติว่าเรามีหลายเธรด เธรดแรกจะวนซ้ำ และเธรดที่สองจะแทรกหรือลบองค์ประกอบออกจากคอลเลกชันเดียวกัน อย่างไรก็ตาม เราจะได้รับConcurrentModificationExceptionเมื่อทำงานในสภาพแวดล้อมแบบเธรดเดียว:List<String> cities = new ArrayList<>();
cities.add(“Warsaw”);
cities.add(“Prague”);
cities.add(“Budapest”);
Iterator<String> cityIterator = cities.iterator();
cityIterator.next();
cities.remove(1);
cityIterator.next(); // генерирует ConcurrentModificationException
ล้มเหลวอย่างรวดเร็ว
ส่วนของโค้ดด้านบนเป็นตัวอย่างของตัววนซ้ำที่ล้มเหลวอย่างรวดเร็ว อย่างที่คุณเห็น ConcurrentModificationExceptionถูกส่งออกมาเมื่อพยายามดึงองค์ประกอบที่สองจากตัววนซ้ำ ตัววนซ้ำจะรู้ได้อย่างไรว่าคอลเลกชันได้รับการแก้ไขตั้งแต่ถูกสร้างขึ้น ตัวอย่างเช่น คอลเลกชันอาจมีการประทับวันที่/เวลาเช่นLastModified เมื่อสร้างตัววนซ้ำ คุณควรคัดลอกฟิลด์นี้และจัดเก็บไว้ในออบเจ็กต์ตัววนซ้ำ จากนั้น แต่ละครั้งที่คุณเรียกใช้ เมธอด next()คุณจะเปรียบเทียบ ค่า LastModifiedจากคอลเลกชันกับสำเนาจากตัววนซ้ำ มีการใช้วิธีการที่คล้ายกันมากในการดำเนินการของคลาสArrayList มีตัวแปรอินสแตนซ์modCountที่เก็บจำนวนครั้งที่รายการได้รับการแก้ไข:final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
สิ่งสำคัญคือต้องสังเกตว่า ตัววนซ้ำ ที่เร็วเมื่อล้มเหลวทำงานบนพื้นฐานที่ดีที่สุด ซึ่งหมายความว่าไม่มีการรับประกันว่าConcurrentModificationException จะถูกส่งออกไป ในกรณีที่มีการแก้ไขพร้อมกัน ดังนั้นคุณไม่ควรพึ่งพาสิ่งเหล่านี้ แต่ควรใช้เพื่อตรวจจับข้อผิดพลาด คอลเลกชันที่ไม่เกิดขึ้นพร้อมกันส่วนใหญ่มีตัววน ซ้ำที่ล้มเหลวอย่างรวดเร็ว
ความสม่ำเสมอที่อ่อนแอ
คอลเลกชันที่เกิดขึ้นพร้อมกันส่วนใหญ่ใน แพ็คเกจ java.util.concurrent (เช่นConcurrentHashMapและQueue ส่วนใหญ่ ) จัดให้มีตัววนซ้ำที่มีความสอดคล้องน้อย ความหมายของคำนี้อธิบายไว้อย่างดีในเอกสารประกอบ :- สามารถประมวลผลพร้อมกับการดำเนินการอื่นๆ ได้
- พวกเขาไม่เคยโยนConcurrentModificationException
- รับประกันว่าจะสำรวจองค์ประกอบที่มีอยู่ ณ เวลาที่ตัววนซ้ำถูกสร้างขึ้นเพียงครั้งเดียว และสามารถ (แต่ไม่จำเป็น) สะท้อนถึงการแก้ไขในภายหลัง
GO TO FULL VERSION