Varlıklarımı güncellerken "ConcurrentModificationException"
Sorun
Hazırda bekletme 5.4.11 çekirdeğini kullanıyorum ve büyük miktarda varlığı güncellemeye çalıştığımda tam burada bu sorunla karşılaşıyorum. Hazırda bekletme dahili istisnası gibi görünüyor.
Güncelleme
Hazırda bekletme 5.4.23 ile test ettim, aynı istisna oluştu. Ayrıca "mysql konektörümü" en son "8.0.22" sürümüne güncelledim ve aynı istisna olarak çalışmadı.Açıklama
Varlıklarımı böyle güncellerim, başka bir iş parçacığında çalışır, ki burada sorun olmamalıdır ... temelde veritabanı işlemleri için kendi iş parçacığıdır, bu işlem sırasında başka hiçbir veritabanı işlemi çalışmaz.
// Run the database operation for updating the entities async in a new thread, return updated entities once done
return CompletableFuture.runAsync(() -> {
var session = database.openSession();
session.beginTransaction();
try {
// Save identities first
for (var identity : identities)
session.update(identity);
// Update components, each component-type
for(var insertedClass : insertationOrder)
for (var hbc : componentsPerType.get(insertedClass))
session.update(hbc);
session.flush();
session.clear();
session.getTransaction().commit();
} catch (Exception e){
var messageComposer = new ExceptionMessageComposer(e);
GameExtension.getInstance().trace("Update : "+messageComposer.toString());
session.getTransaction().rollback();
}
session.close();
}).thenApply(v -> entities);
Kurtarmaya çalıştığım sınıf
@Entity
@Table(name = "chunk", uniqueConstraints = {@UniqueConstraint(columnNames={"x", "y"})}, indexes = {@Index(columnList = "x,y")})
@Access(value = AccessType.FIELD)
@SelectBeforeUpdate(false)
public class Chunk extends HibernateComponent{
public int x;
public int y;
public Date createdOn;
@OneToMany(fetch = FetchType.EAGER)
@JoinTable(name = "chunk_identity", joinColumns = @JoinColumn(name = "identity_id"), inverseJoinColumns = @JoinColumn(name = "id"), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
@Fetch(FetchMode.JOIN)
@BatchSize(size = 50)
public Set<Identity> inChunk = new LinkedHashSet<>();
@Transient
public Set<ChunkLoader> loadedBy = new LinkedHashSet<>();
public Chunk() {}
public Chunk(int x, int y, Date createdOn) {
this.x = x;
this.y = y;
this.createdOn = createdOn;
}
}
@Entity
@Table(name = "location")
@Access(AccessType.FIELD)
public class Transform extends HibernateComponent {
@Embedded
public Vector2 position = new Vector2();
@ManyToOne
@JoinColumn(name = "chunk_id", referencedColumnName = "identity_id", updatable = false)
@Fetch(FetchMode.JOIN)
public Chunk chunk;
@Transient
public Vector2 previousPos = new Vector2();
public Transform() {}
public Transform(Vector2 position) { this.position = position; }
public Transform(Vector2 position, Chunk chunk) { this.position = position; this.chunk = chunk; }
}
Aldığım istisna
19:29:05,126 INFO [ForkJoinPool.commonPool-worker-19] Extensions - {Game}: Update : java.util.ConcurrentModificationException:
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Exception: java.util.ConcurrentModificationException
Message: *** Null ***
+--- --- ---+
Stack Trace:
+--- --- ---+
java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1493) java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1516)
org.hibernate.persister.collection.AbstractCollectionPersister.insertRows(AbstractCollectionPersister.java:1555)
org.hibernate.action.internal.CollectionUpdateAction.execute(CollectionUpdateAction.java:89)
org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:478)
java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:475)
org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:348)
org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:40)
org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102)
org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1356)
org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1343)
com.parallelorigin.persistence.database.ECSDatabase.lambda$updateEntities$16(ECSDatabase.java:242)
java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736) java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Hazırda bekletme modunun neden bu dahili istisnayı attığını ve bu sorunu çözmek için ne yapabileceğimi bilen var mı?
Yanıtlar
Sen listesini değiştirmemelidir entities, identitiesya componentssen / güncelleme rağmen devam eden başka bir iş parçacığı üzerinde çalışırken.
Devam ederse / güncellemeler varlıklar gerektiğini iplik kendi varlık nesneleri işlem çalışırken başka hiçbir iplik durumuna engel olabilir yani. Nesneleri ancak işlem bittikten sonra tekrar kullanabilirsiniz.
Uygulamanızın ne yaptığını bilmiyorum, ancak ana iş parçacığınızdaki nesnelere erişmeniz gerekiyorsa, nesneleri temizleme işlemi için kopyalayabilirsiniz. Bu şekilde, sifonu yapan iş parçacığı nesnelere uygun şekilde sahip olur ve bunlar üzerinde güvenle çalışabilir.