หากฉันมีเธรดหลายเธรดที่เข้าถึง HashMap แต่รับประกันได้ว่าพวกเขาจะไม่เข้าถึงคีย์เดียวกันในเวลาเดียวกันนั่นอาจทำให้เกิดสภาวะการแข่งขันได้หรือไม่
หากฉันมีเธรดหลายเธรดที่เข้าถึง HashMap แต่รับประกันได้ว่าพวกเขาจะไม่เข้าถึงคีย์เดียวกันในเวลาเดียวกันนั่นอาจทำให้เกิดสภาวะการแข่งขันได้หรือไม่
คำตอบ:
คำตอบของ In @ dotsid เขาบอกว่า:
หากคุณเปลี่ยน HashMap ด้วยวิธีใดก็ตามโค้ดของคุณจะเสีย
เขาถูกต้อง HashMap ที่อัปเดตโดยไม่มีการซิงโครไนซ์จะหยุดทำงานแม้ว่าเธรดกำลังใช้ชุดคีย์ที่ไม่ต่อกัน นี่คือบางส่วนของสิ่งที่อาจผิดพลาด
หากเธรดหนึ่งเธรดหนึ่งเธรดput
อื่นอาจเห็นค่าเก่าสำหรับขนาดของแฮชแมป
เมื่อเธรดput
ทำการสร้างตารางขึ้นมาใหม่เธรดอื่นอาจเห็นการอ้างอิงอาร์เรย์แฮชแท็กเวอร์ชันชั่วคราวหรือเก่าขนาดเนื้อหาหรือแฮชเชน ความโกลาหลอาจตามมา
เมื่อเธรดทำput
คีย์ที่ชนกับคีย์ที่ใช้โดยเธรดอื่นและเธรดหลังทำput
คีย์ของเธรดหลังอาจเห็นสำเนาเก่าของการอ้างอิงแฮชเชน ความโกลาหลอาจตามมา
เมื่อเธรดหนึ่งตรวจสอบตารางด้วยคีย์ที่ชนกับคีย์ของเธรดอื่น ๆ บางคีย์อาจพบคีย์นั้นบนโซ่ มันจะเรียกเท่ากับบนคีย์นั้นและหากเธรดไม่ซิงโครไนซ์เมธอด equals อาจพบสถานะเก่าในคีย์นั้น
และหากคุณมีสองเธรดที่ทำพร้อมกันput
หรือremove
ร้องขอก็มีโอกาสมากมายสำหรับเงื่อนไขการแข่งขัน
ฉันคิดได้สามวิธี:
ConcurrentHashMap
.HashMap
แต่ซิงโครไนซ์กับภายนอก เช่นการใช้ mutexes ดั้งเดิมLock
วัตถุ ฯลฯHashMap
สำหรับแต่ละเธรด หากเธรดมีชุดคีย์ที่ไม่ปะติดปะต่อกันจริงๆก็ไม่จำเป็นต้องมี (จากมุมมองของอัลกอริทึม) เพื่อให้พวกเขาแชร์แผนที่เดียว อันที่จริงหากอัลกอริทึมของคุณเกี่ยวข้องกับเธรดที่วนซ้ำคีย์ค่าหรือรายการของแผนที่ในบางจุดการแยกแผนที่เดียวออกเป็นหลายแผนที่อาจทำให้เกิดการเร่งความเร็วอย่างมากสำหรับส่วนนั้นของการประมวลผลเพียงใช้ ConcurrentHashMap ConcurrentHashMap ใช้การล็อกหลายรายการซึ่งครอบคลุมช่วงของที่เก็บข้อมูลแฮชเพื่อลดโอกาสในการโต้แย้งการล็อก มีผลกระทบต่อประสิทธิภาพเล็กน้อยในการได้มาซึ่งการล็อกที่ไม่มีใครโต้แย้ง
เพื่อตอบคำถามเดิมของคุณ: ตามที่ javadoc ตราบใดที่โครงสร้างของแผนที่ไม่เปลี่ยนแปลงคุณก็สบายดี ซึ่งหมายความว่าไม่มีการลบองค์ประกอบใด ๆ เลยและไม่มีการเพิ่มคีย์ใหม่ที่ไม่มีอยู่ในแผนที่ การแทนที่ค่าที่เกี่ยวข้องกับคีย์ที่มีอยู่นั้นทำได้ดี
หากเธรดหลายเธรดเข้าถึงแฮชแม็พพร้อมกันและเธรดอย่างน้อยหนึ่งเธรดแก้ไขแม็พเชิงโครงสร้างเธรดนั้นจะต้องซิงโครไนซ์ภายนอก (การปรับเปลี่ยนโครงสร้างคือการดำเนินการใด ๆ ที่เพิ่มหรือลบการแมปอย่างน้อยหนึ่งรายการการเปลี่ยนค่าที่เชื่อมโยงกับคีย์ที่อินสแตนซ์มีอยู่แล้วเท่านั้นไม่ใช่การแก้ไขโครงสร้าง)
แม้ว่าจะไม่มีการรับประกันเกี่ยวกับการมองเห็น ดังนั้นคุณต้องเต็มใจที่จะยอมรับการดึงข้อมูลการเชื่อมโยงเก่า ๆ เป็นครั้งคราว
ขึ้นอยู่กับความหมายของคุณภายใต้ "การเข้าถึง" หากคุณเพิ่งอ่านคุณสามารถอ่านได้แม้กระทั่งคีย์เดียวกันตราบใดที่การเปิดเผยข้อมูลรับรองภายใต้กฎ" เกิดขึ้นก่อน " ซึ่งหมายความว่าHashMap
ไม่ควรเปลี่ยนและการเปลี่ยนแปลงทั้งหมด (การก่อสร้างเริ่มต้น) ควรจะเสร็จสิ้นก่อนที่จะอ่านใด ๆ HashMap
เริ่มต้นที่จะเข้าถึง
หากคุณเปลี่ยนHashMap
วิธีใด ๆ แสดงว่ารหัสของคุณเสีย @Stephen C ให้คำอธิบายที่ดีมากว่าทำไม
แก้ไข: หากกรณีแรกเป็นสถานการณ์จริงของคุณฉันขอแนะนำให้คุณใช้Collections.unmodifiableMap()
เพื่อให้แน่ใจว่า HashMap ของคุณจะไม่มีการเปลี่ยนแปลง วัตถุที่ชี้โดยHashMap
ไม่ควรเปลี่ยนแปลงเช่นกันดังนั้นการใช้final
คำหลักเชิงรุกสามารถช่วยคุณได้
และอย่างที่ @Lars Andren กล่าวว่าConcurrentHashMap
เป็นทางเลือกที่ดีที่สุดในกรณีส่วนใหญ่
unmodifiableMap
ทำให้แน่ใจว่าลูกค้าไม่สามารถเปลี่ยนแผนที่ได้ ไม่ดำเนินการใด ๆ เพื่อให้แน่ใจว่าจะไม่มีการเปลี่ยนแปลงแผนที่ต้นแบบ
การแก้ไข HashMap โดยไม่มีการซิงโครไนซ์อย่างเหมาะสมจากสองเธรดอาจทำให้เกิดสภาวะการแย่งชิงได้ง่าย
put()
นำไปสู่การปรับขนาดของตารางภายในการดำเนินการนี้จะใช้เวลาสักครู่และเธรดอื่นจะยังคงเขียนลงในตารางเก่าput()
สำหรับคีย์ที่แตกต่างกันจะนำไปสู่การอัปเดตที่เก็บข้อมูลเดียวกันหากรหัสแฮชของคีย์มีขนาดเท่ากันสำหรับตาราง (อันที่จริงความสัมพันธ์ระหว่าง hashcode และ bucket index นั้นซับซ้อนกว่า แต่ก็อาจเกิดการชนกันได้)HashMap
การนำไปใช้งานที่คุณใช้คุณอาจได้รับความเสียหายของHashMap
โครงสร้างข้อมูลเป็นต้นสาเหตุจากความผิดปกติของหน่วยความจำ