Jme3 Beginner: Multithreading for Beginners (ConcurrentModificationException)

Hello all,



so In a previous topic I asked about multi-threading : http://hub.jmonkeyengine.org/groups/general-2/forum/topic/jme3-beginner-using-threads/?topic_page=3&num=15



Now that I have 2 threads running in the following format:



[java] THREAD 1:



read from file (time 0 until time 30)

{

run animations…

}



THREAD 2:



read from file (time 31 until time 100000)

{

run animations…

}[/java]



I am getting this error occasionally :



[java]

SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]

java.util.ConcurrentModificationException

at java.util.AbstractList$Itr.checkForComodification(Unknown Source)

at java.util.AbstractList$Itr.next(Unknown Source)

at com.jme3.cinematic.Cinematic.initialize(Cinematic.java:188)

at com.jme3.app.state.AppStateManager.update(AppStateManager.java:150)

at com.jme3.app.SimpleApplication.update(SimpleApplication.java:252)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:149)

at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:223)

at java.lang.Thread.run(Unknown Source)

Oct 26, 2011 2:46:32 PM com.jme3.renderer.lwjgl.LwjglRenderer cleanup

INFO: Deleting objects and invalidating state

Oct 26, 2011 2:46:32 PM com.jme3.input.lwjgl.LwjglMouseInput destroy[/java]



From previous posts, the stack trace indicates that I am adding events from another event (or events from another thread). What I do is really just read a trace file. The error does not occur everytime, mostly after I run it the first time and want to run the animation another time I get it (as if some objects are not being destroyed).



For creating the 2nd thread I am using:



[java]/** Separate thread that reads the rest of the file concurrently with the execution */

try

{

//If we have not started a callable yet, do so!

if(adapter == null && future == null)

{

//Thread starts!

executor.submit(fileLoader); // must be a callable

}

//If we have started a callable already, we check the status

else if(future != null)

{

//Get the thread data when its done

if(future.isDone())

{

adapter = (Jme3Adapter) future.get();

future = null;



}

else if(future.isCancelled())

{

//Set future to null. Maybe we succeed next time…

future = null;

}



}

}

catch(Exception e){

e.printStackTrace();

}[/java]



and



[java]

@Override

public void destroy(){

super.destroy();

executor.shutdown();

}[/java]

Something I’ve noticed:



[java]//Get the thread data when its done

if(future.isDone())

{

adapter = (Jme3Adapter) future.get();

future = null;

System.out.println(“Thread 2 completed!!!”); >>>>>>>>>>>> never called even after thread is done

[/java]



nor this one:



[java] if(adapter != null){

System.out.println(“Success!!!”); <<<< never called

}

[/java]



Also this problem occurs EXACTLY when switching from thread 1 to thread 2 (but again NOT always - very unpredictable)



Finally, if I wanted to debug it (I am using Eclipse), how can I print the trace for the second trace? They are both using the same class (ADAPTER) that reads the file so for example I pring when the 1st thread reads the file:



[java]Time read so far: 1.7310

Time read so far: 1.7990

Time read so far: 1.8540

Time read so far: 1.9760[/java]



until time 10 then thread 2 continues from there however, it does not print the trace (file being read concurrently)

You, or the methods you call inside the threads use a List. Lists are not threadsafe by default. You have to use a synchronizedList instead. BTW, you can modify the scenegraph only from the main thread

But that defies the idea of using thread in my example if I can’t do so right? I am trying to read a very large file (trace for animation) and I thought I could divide reading into chunks. Are you saying I cant read the 2nd portion of it and execute on the fly using threads??

executor.submit(fileLoader); should return a future but it doesn’t look like you assign it, that means it will stay null forever. apart from that the callable you submit contains a logical error otherwise you wouldn’t get the exception.

garnaout said:
But that defies the idea of using thread in my example if I can't do so right? I am trying to read a very large file (trace for animation) and I thought I could divide reading into chunks. Are you saying I cant read the 2nd portion of it and execute on the fly using threads??


You can use as many threads as you want, with the threads you can preload the data, but you have to apply the data to the scenegraph within the mainloop.
executor.submit(fileLoader); should return a future but it doesn’t look like you assign it, that means it will stay null forever.


could you explain this idea more?

zzueg I switched to SynchronizedList (and thanks for that as it is the better approach to do what I am trying to do) however, that didn't solve it.

Note that all this code:



[java]readFile(0 , 10.0f); // TODO: to be changed to a generic number



/** Separate thread that reads the rest of the file concurrently with the execution */

try

{

//If we have not started a callable yet, do so!

if(adapter == null && future == null)

{

//Thread starts!

future = executor.submit(fileLoader); // must be a callable

}

//If we have started a callable already, we check the status

else if(future != null)

{

//Get the thread data when its done

if(future.isDone())

{

adapter = (Jme3Adapter) future.get();

future = null;

System.out.println(“Thread is completed!!”);



}

else if(future.isCancelled())

{

//Set future to null. Maybe we succeed next time…

future = null;

}



}

}

catch(Exception e){

e.printStackTrace();

}



if(adapter != null){

System.out.println(“Adapter Null - COMPLETE!!!”);

}

[/java]



is not placed in an update () function (and that explains why I don’t get notified when the 2nd thread is done executing)



it’s all available in the simpleInitApp() // is that wrong?

It sounds like you are adding or removing Cinematics to the list while the system is iterating over them. You will have to queue up adds/removes to the cinematic events outside of its initialize() method.



Also your threading is wrong, you won’t catch the future.isDone() ever (unless very lucky)…

[java]future = executor.submit(fileLoader);[/java]

…is NOT a blocking call.

Future.get() is a blocking call, meaning that the thread will execute and finish, then return. Just submitting the thread does not run it immediately.

If you want to wait until that thread is done, you will need a while loop checking if future.isDone(), possibly sleeping between iterations.