Varlıklarımı güncellerken "ConcurrentModificationException"

Nov 06 2020

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

3 ChristianBeikov Nov 07 2020 at 10:43

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.