That's the idea(bolded). The simulation should run at 60 logical FPS even if the actual FPS could be 60 to 1000+.
I understood that was the idea. However, you're not measuring anything useful otherwise (see next point).
AFAIK, there's also just a single thread that updates and draws. Care to explain where am I sharing data between threads?
repaint() does not call paint(). It queues up a repaint request on the Swing event dispatch thread - it's asynchronous
and returns immediately. Therefore you're updating the image in update() in the thread you create, and accessing it from the EDT in paint().
Also, as repaint() returns immediately, you're not actually measuring FPS because you're at no point measuring the actual cost of rendering to the screen.
You need to look at that active rendering loop I linked to.