2009-03-02 6 views
94

Tôi đang sử dụng các đối tượng bền bỉ bằng JPA. Đối tượng chính có mối quan hệ One-Many với một đối tượng khác. Đối tượng khác được lưu trữ trong HashMap. Loại đồng bộ hóa nào sẽ khắc phục vấn đề này? Nó dường như xảy ra vào thời điểm hoàn toàn ngẫu nhiên và rất khó đoán trước. Dưới đây là ngoại lệ tôi nhận được:ConcurrentModificationException và HashMap

Exception in thread "pool-1-thread-1" java.util.ConcurrentModificationException 
     at java.util.HashMap$HashIterator.nextEntry(Unknown Source) 
     at java.util.HashMap$ValueIterator.next(Unknown Source) 
     at org.hibernate.collection.AbstractPersistentCollection$IteratorProxy.next(AbstractPersistentCollection.java:555) 
     at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296) 
     at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242) 
     at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219) 
     at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169) 
     at org.hibernate.engine.Cascade.cascade(Cascade.java:130) 
+1

bạn có thể cung cấp một số đồng hơn ntext? Bạn có đang hợp nhất, cập nhật hoặc xóa một thực thể không? Những gì các tổ chức doest thực thể này có? Điều gì về cài đặt xếp tầng của bạn? – ordnungswidrig

+0

Từ theo dõi ngăn xếp, bạn có thể thấy rằng Ngoại lệ xảy ra trong khi lặp qua HashMap. Chắc chắn một số chủ đề khác đang sửa đổi bản đồ nhưng ngoại lệ xảy ra trong chuỗi đang lặp lại. – Chochos

+0

Có thể trùng lặp của [Iterating thông qua một bộ sưu tập, tránh ConcurrentModificationException khi loại bỏ trong vòng lặp] (http://stackoverflow.com/questions/223918/iterating-through-a-collection-avoiding-concurrentmodificationexception-when-re) – Raedwald

Trả lời

205

Đây không phải là sự cố đồng bộ hóa. Điều này sẽ xảy ra nếu bộ sưu tập cơ bản đang được lặp lại bị sửa đổi bởi bất kỳ thứ gì khác ngoài bản Iterator.

Iterator it = map.entrySet().iterator(); 
while (it.hasNext()) 
{ 
    Entry item = it.next(); 
    map.remove(item.getKey()); 
} 

Điều này sẽ ném một ConcurrentModificationException khi nó.hasNext() được gọi là lần thứ hai.

Cách tiếp cận đúng sẽ là

Iterator it = map.entrySet().iterator(); 
    while (it.hasNext()) 
    { 
     Entry item = it.next(); 
     it.remove(); 
    } 

Giả sử iterator này hỗ trợ các hoạt động xóa().

+1

Có thể, nhưng có vẻ như Hibernate đang thực hiện lặp lại, nên được triển khai một cách hợp lý một cách chính xác. Có thể có gọi lại sửa đổi bản đồ, nhưng điều đó là không thể. Tính không tiên đoán chỉ ra một vấn đề đồng thời thực tế. –

+0

Ngoại lệ này không liên quan gì đến việc đồng thời luồng, điều này là do cửa hàng sao lưu của trình lặp được sửa đổi. Cho dù bởi một luồng khác không quan trọng với trình lặp. IMHO nó là một ngoại lệ được đặt tên kém vì nó đưa ra một ấn tượng không chính xác về nguyên nhân. – Robin

+0

Tuy nhiên, tôi đồng ý rằng nếu không thể đoán trước được, có nhiều khả năng là vấn đề về luồng gây ra các điều kiện cho ngoại lệ này xảy ra. Mà làm cho nó tất cả các khó hiểu hơn vì tên ngoại lệ. – Robin

3

Nghe có vẻ như vấn đề đồng bộ hóa Java và giống như vấn đề khóa cơ sở dữ liệu.

Tôi không biết nếu thêm phiên bản vào tất cả các lớp liên tục của bạn sẽ sắp xếp nó ra, nhưng đó là một cách mà Hibernate có thể cung cấp quyền truy cập độc quyền vào các hàng trong bảng.

Có thể mức độ cách ly đó cần phải cao hơn. Nếu bạn cho phép "đọc bẩn", có thể bạn cần phải bump lên để serializable.

+0

HashMap là chủ đề - an toàn. Vấn đề không sunchronization của nó. – TBH

+1

Bạn có bỏ phiếu cho tôi không? Bạn không đọc câu trả lời của tôi? Tôi nói đó là một vấn đề cơ sở dữ liệu khóa, không phải là "đồng bộ hóa". Kỹ năng đọc của bạn kém như chính tả của bạn. – duffymo

+1

@TBH Làm thế nào là HashMap thread-safe? –

1

Hãy thử CopyOnWriteArrayList hoặc CopyOnWriteArraySet tùy thuộc vào những gì bạn đang cố gắng thực hiện.

51

Hãy thử sử dụng một ConcurrentHashMap thay vì một HashMap đồng bằng

+0

Điều đó thực sự giải quyết được vấn đề? Tôi đang gặp vấn đề tương tự nhưng tôi chắc chắn có thể loại trừ bất kỳ vấn đề luồng nào. – tobiasbayer

+2

Một giải pháp khác là tạo bản sao của bản đồ và lặp lại thông qua bản sao đó. Hoặc sao chép bộ khóa và lặp qua chúng, nhận giá trị cho mỗi khóa từ bản đồ gốc. – Chochos

+0

Đó là Hibernate đang lặp lại thông qua bộ sưu tập để bạn không thể chỉ đơn giản là sao chép nó. – tobiasbayer

-1

Có lẽ giải pháp khác sẽ được tiếp thu một khóa trước khi bắt đầu sửa đổi của bạn/bền bỉ để không có một số chủ đề khác sửa đổi những gì bạn đang lặp

private ReadWriteLock lock = new ReentrantReadWriteLock(); 
lock.writeLock().lock(); 
try{ 
//itterate and persist 
} 
finally{ 
lock.writeLock().unlock(); 
    } 
  • Nếu bạn không làm bất kỳ thao tác có thể lock.readLock khóa()(). cũng là OK