เธรด HashMap ปลอดภัยสำหรับคีย์อื่นหรือไม่


88

หากฉันมีเธรดหลายเธรดที่เข้าถึง HashMap แต่รับประกันได้ว่าพวกเขาจะไม่เข้าถึงคีย์เดียวกันในเวลาเดียวกันนั่นอาจทำให้เกิดสภาวะการแข่งขันได้หรือไม่

คำตอบ:


100

คำตอบของ In @ dotsid เขาบอกว่า:

หากคุณเปลี่ยน HashMap ด้วยวิธีใดก็ตามโค้ดของคุณจะเสีย

เขาถูกต้อง HashMap ที่อัปเดตโดยไม่มีการซิงโครไนซ์จะหยุดทำงานแม้ว่าเธรดกำลังใช้ชุดคีย์ที่ไม่ต่อกัน นี่คือบางส่วนของสิ่งที่อาจผิดพลาด

  • หากเธรดหนึ่งเธรดหนึ่งเธรดputอื่นอาจเห็นค่าเก่าสำหรับขนาดของแฮชแมป

  • เมื่อเธรดputทำการสร้างตารางขึ้นมาใหม่เธรดอื่นอาจเห็นการอ้างอิงอาร์เรย์แฮชแท็กเวอร์ชันชั่วคราวหรือเก่าขนาดเนื้อหาหรือแฮชเชน ความโกลาหลอาจตามมา

  • เมื่อเธรดทำputคีย์ที่ชนกับคีย์ที่ใช้โดยเธรดอื่นและเธรดหลังทำputคีย์ของเธรดหลังอาจเห็นสำเนาเก่าของการอ้างอิงแฮชเชน ความโกลาหลอาจตามมา

  • เมื่อเธรดหนึ่งตรวจสอบตารางด้วยคีย์ที่ชนกับคีย์ของเธรดอื่น ๆ บางคีย์อาจพบคีย์นั้นบนโซ่ มันจะเรียกเท่ากับบนคีย์นั้นและหากเธรดไม่ซิงโครไนซ์เมธอด equals อาจพบสถานะเก่าในคีย์นั้น

และหากคุณมีสองเธรดที่ทำพร้อมกันputหรือremoveร้องขอก็มีโอกาสมากมายสำหรับเงื่อนไขการแข่งขัน

ฉันคิดได้สามวิธี:

  1. ใช้ไฟล์ConcurrentHashMap.
  2. ใช้ปกติHashMapแต่ซิงโครไนซ์กับภายนอก เช่นการใช้ mutexes ดั้งเดิมLockวัตถุ ฯลฯ
  3. ใช้ที่แตกต่างกันHashMapสำหรับแต่ละเธรด หากเธรดมีชุดคีย์ที่ไม่ปะติดปะต่อกันจริงๆก็ไม่จำเป็นต้องมี (จากมุมมองของอัลกอริทึม) เพื่อให้พวกเขาแชร์แผนที่เดียว อันที่จริงหากอัลกอริทึมของคุณเกี่ยวข้องกับเธรดที่วนซ้ำคีย์ค่าหรือรายการของแผนที่ในบางจุดการแยกแผนที่เดียวออกเป็นหลายแผนที่อาจทำให้เกิดการเร่งความเร็วอย่างมากสำหรับส่วนนั้นของการประมวลผล

31

เพียงใช้ ConcurrentHashMap ConcurrentHashMap ใช้การล็อกหลายรายการซึ่งครอบคลุมช่วงของที่เก็บข้อมูลแฮชเพื่อลดโอกาสในการโต้แย้งการล็อก มีผลกระทบต่อประสิทธิภาพเล็กน้อยในการได้มาซึ่งการล็อกที่ไม่มีใครโต้แย้ง

เพื่อตอบคำถามเดิมของคุณ: ตามที่ javadoc ตราบใดที่โครงสร้างของแผนที่ไม่เปลี่ยนแปลงคุณก็สบายดี ซึ่งหมายความว่าไม่มีการลบองค์ประกอบใด ๆ เลยและไม่มีการเพิ่มคีย์ใหม่ที่ไม่มีอยู่ในแผนที่ การแทนที่ค่าที่เกี่ยวข้องกับคีย์ที่มีอยู่นั้นทำได้ดี

หากเธรดหลายเธรดเข้าถึงแฮชแม็พพร้อมกันและเธรดอย่างน้อยหนึ่งเธรดแก้ไขแม็พเชิงโครงสร้างเธรดนั้นจะต้องซิงโครไนซ์ภายนอก (การปรับเปลี่ยนโครงสร้างคือการดำเนินการใด ๆ ที่เพิ่มหรือลบการแมปอย่างน้อยหนึ่งรายการการเปลี่ยนค่าที่เชื่อมโยงกับคีย์ที่อินสแตนซ์มีอยู่แล้วเท่านั้นไม่ใช่การแก้ไขโครงสร้าง)

แม้ว่าจะไม่มีการรับประกันเกี่ยวกับการมองเห็น ดังนั้นคุณต้องเต็มใจที่จะยอมรับการดึงข้อมูลการเชื่อมโยงเก่า ๆ เป็นครั้งคราว


6

ขึ้นอยู่กับความหมายของคุณภายใต้ "การเข้าถึง" หากคุณเพิ่งอ่านคุณสามารถอ่านได้แม้กระทั่งคีย์เดียวกันตราบใดที่การเปิดเผยข้อมูลรับรองภายใต้กฎ" เกิดขึ้นก่อน " ซึ่งหมายความว่าHashMapไม่ควรเปลี่ยนและการเปลี่ยนแปลงทั้งหมด (การก่อสร้างเริ่มต้น) ควรจะเสร็จสิ้นก่อนที่จะอ่านใด ๆ HashMapเริ่มต้นที่จะเข้าถึง

หากคุณเปลี่ยนHashMapวิธีใด ๆ แสดงว่ารหัสของคุณเสีย @Stephen C ให้คำอธิบายที่ดีมากว่าทำไม

แก้ไข: หากกรณีแรกเป็นสถานการณ์จริงของคุณฉันขอแนะนำให้คุณใช้Collections.unmodifiableMap()เพื่อให้แน่ใจว่า HashMap ของคุณจะไม่มีการเปลี่ยนแปลง วัตถุที่ชี้โดยHashMapไม่ควรเปลี่ยนแปลงเช่นกันดังนั้นการใช้finalคำหลักเชิงรุกสามารถช่วยคุณได้

และอย่างที่ @Lars Andren กล่าวว่าConcurrentHashMapเป็นทางเลือกที่ดีที่สุดในกรณีส่วนใหญ่


2
ConcurrentHashMap เป็นตัวเลือกที่ดีที่สุดในความคิดของฉัน เหตุผลเดียวที่ฉันไม่แนะนำเพราะผู้เขียนไม่ได้ถาม :) มันมีปริมาณงานน้อยลงเนื่องจากการดำเนินการของ CAS แต่ตามกฎทองของการเขียนโปรแกรมพร้อมกันกล่าวว่า: "ทำให้ถูกต้องแล้วทำให้เร็วเท่านั้น ":)
Denis Bazhenov

unmodifiableMapทำให้แน่ใจว่าลูกค้าไม่สามารถเปลี่ยนแผนที่ได้ ไม่ดำเนินการใด ๆ เพื่อให้แน่ใจว่าจะไม่มีการเปลี่ยนแปลงแผนที่ต้นแบบ
Pete Kirkham

ดังที่ได้กล่าวไปแล้ว: "วัตถุที่ HashMap ชี้ก็ไม่ควรเปลี่ยนแปลงเช่นกัน"
Denis Bazhenov

4

การแก้ไข HashMap โดยไม่มีการซิงโครไนซ์อย่างเหมาะสมจากสองเธรดอาจทำให้เกิดสภาวะการแย่งชิงได้ง่าย

  • เมื่อput()นำไปสู่การปรับขนาดของตารางภายในการดำเนินการนี้จะใช้เวลาสักครู่และเธรดอื่นจะยังคงเขียนลงในตารางเก่า
  • สองput()สำหรับคีย์ที่แตกต่างกันจะนำไปสู่การอัปเดตที่เก็บข้อมูลเดียวกันหากรหัสแฮชของคีย์มีขนาดเท่ากันสำหรับตาราง (อันที่จริงความสัมพันธ์ระหว่าง hashcode และ bucket index นั้นซับซ้อนกว่า แต่ก็อาจเกิดการชนกันได้)

1
มันแย่กว่าแค่สภาพการแข่งขัน ขึ้นอยู่กับการใช้งานภายในของHashMapการนำไปใช้งานที่คุณใช้คุณอาจได้รับความเสียหายของHashMapโครงสร้างข้อมูลเป็นต้นสาเหตุจากความผิดปกติของหน่วยความจำ
Stephen C
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.