JavaRush /จาวาบล็อก /Random-TH /เรื่องราวของสอง Iterators: กลยุทธ์การปรับเปลี่ยนการแข่งขั...

เรื่องราวของสอง Iterators: กลยุทธ์การปรับเปลี่ยนการแข่งขันใน Java

เผยแพร่ในกลุ่ม
ผู้เขียนบันทึกนี้คือ Grzegorz Mirek ผู้พัฒนาซอฟต์แวร์จากคราคูฟ (โปแลนด์) เขาเริ่มพัฒนาทักษะในชวาเมื่อประมาณ 6 ปีที่แล้ว ขณะที่ยังเรียนอยู่ในมหาวิทยาลัย และตั้งแต่นั้นเป็นต้นมา เขาได้ขัดเกลาทักษะในด้านนี้อย่างไม่รู้จักเหน็ดเหนื่อย เขาสนใจเป็นพิเศษในเรื่องประสิทธิภาพและการเพิ่ม ประสิทธิภาพของ JVM ซึ่งเป็นสิ่งที่เขาเขียนเกี่ยวกับในบล็อก ของเขาเป็นหลัก
เรื่องราวของสอง Iterators: กลยุทธ์การปรับเปลี่ยนการแข่งขันใน Java - 1
คำถามสัมภาษณ์ 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
  • รับประกันว่าจะสำรวจองค์ประกอบที่มีอยู่ ณ เวลาที่ตัววนซ้ำถูกสร้างขึ้นเพียงครั้งเดียว และสามารถ (แต่ไม่จำเป็น) สะท้อนถึงการแก้ไขในภายหลัง

สแนปชอต

ด้วยกลยุทธ์นี้ ตัววนซ้ำจะเชื่อมโยงกับสถานะของคอลเลกชันในขณะที่สร้าง - นี่คือภาพรวมของคอลเลกชัน การเปลี่ยนแปลงใด ๆ ที่ทำกับคอลเลกชันดั้งเดิมส่งผลให้มีการสร้างโครงสร้างข้อมูลพื้นฐานเวอร์ชันใหม่ ซึ่งจะทำให้สแนปชอตของเราไม่เปลี่ยนแปลง ดังนั้นจึงไม่สะท้อนถึงการเปลี่ยนแปลงในคอลเลกชันที่เกิดขึ้นหลังจากสร้างตัววนซ้ำ นี่เป็นเทคนิค copy-on-write (COW) แบบเก่าที่ดี จะช่วยแก้ปัญหาการแก้ไขที่เกิดขึ้นพร้อมกันได้อย่างสมบูรณ์ ดังนั้นจึงไม่มีการสร้างConcurrentModificationException ด้วยวิธีนี้ นอกจากนี้ ตัววนซ้ำไม่สนับสนุนการดำเนินการที่เปลี่ยนแปลงองค์ประกอบ คอลเลกชันการคัดลอกเมื่อเขียนมีแนวโน้มที่จะมีราคาแพงเกินไป แต่ก็สมเหตุสมผลที่จะใช้หากการเปลี่ยนแปลงเกิดขึ้นน้อยกว่าการแวะผ่านตัววนซ้ำ ตัวอย่างคือคลาส CopyOnWriteArrayListและCopyOnWriteArraySet

พฤติกรรมที่ไม่ได้กำหนด

คุณอาจพบพฤติกรรมที่ไม่ได้กำหนดไว้ในคอลเลกชันประเภท เดิมเช่นVectorและHashtable ทั้งสองมีตัววนซ้ำ แบบเร็วที่ล้มเหลวมาตรฐานแต่นอกจากนี้ ยังอนุญาตให้ใช้งานอิน เทอร์เฟซ การแจงนับได้และพวกเขาไม่ทราบวิธีการทำงานในกรณีที่มีการแก้ไขพร้อมกัน คุณอาจพบว่าองค์ประกอบบางอย่างเกิดขึ้นซ้ำหรือขาดหายไป หรือแม้แต่มีข้อยกเว้นแปลกๆ บางประการ อย่าเล่นกับพวกมันจะดีกว่า!
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION