Reputation: 431
So I'm actually not sure of what the problem really is. It propably relates to the way I'm handling my threads but I don't know how to fix this.
The program is a simple boid simulation with different "tribed" boids, and I want to have it so that when I make a new tribe it has it's own panel in the GUI. Now this works perfectly before I start the thread but after that it always freezes. I know that Swing is not thread safe but I'm not sure how to fix this problem.
Here's the code for adding the panels:
val tribeBoxPanels = Buffer.empty[TribeBoxPanel]
val tribeFrames = Buffer.empty[TribeSettingFrame]
val addTribeFrame = ChooseTribeFrame.frame
val addFlockingFrame = AddFlockingFrame.frame
def addTribe(tribe: Tribe) = {
pause()
tribeFrames += new TribeSettingFrame(tribe)
tribeBoxPanels += new TribeBoxPanel(tribe)
refcontents
}
private def refcontents = {
top.optionPanel.contents.clear()
top.optionPanel.contents += new BoxPanel(Orientation.Vertical) {
tribeBoxPanels.foreach(contents += _.tribeBoxPanel)
}
top.optionPanel.contents += new BoxPanel(Orientation.Horizontal) {
contents += top.addTribeButton
}
top.optionPanel.contents += new BoxPanel(Orientation.Horizontal) {
contents += top.vectorDebugButton
}
pause()
}
And heres the code for the (runnable) thread:
private var running = true
def pause() = {
if (running) {
running = false
t.stop()
}
else {
running = true
t = new Thread(BoidSimulation)
t.start()
}
}
var t = new Thread(BoidSimulation)
t.start()
Im trying to stop the thread while adding the tribe but that does not seem to work, the GUI still freezes. I also tried t.interrupt() (because that's the better way) but that didn't work also.
EDIT: Could my problem be that I am trying to call the method AddTribe from another object that is not GUI2D (the object the method is in). Maybe if I pasted all of my code to the GUI2D class it would work?
EDIT 2: Tried calling the method like this:
def invoke(tribe: Tribe) = new Runnable() { def run() = addTribe(tribe) }
Didn't help though, the bar that I am trying to add stuff to still freezes.
I also tried printing out the thread that is calling the method and this is what I got:
Thread[Thread-2,5,main] <- println(t)
Thread[AWT-EventQueue-0,6,main] <- This call works
Thread[AWT-EventQueue-0,6,main] <- This call works
Thread[AWT-EventQueue-0,6,main] <- This call works
Thread[AWT-EventQueue-0,6,main] <- After this it freezes.
So the method is being called from the AWT thread but it still freezes the GUI. So the threading is not my problem?
Edit 3: I think I found my problem!! It's quite backwards actually. Because the method is called at a press of a button Swing was trying to complete code that is meant for the calculation thread, that is what caused it to freeze. Now I need to find out the excact opposite of SwingUtilities.invokelater()
Edit 4: Tried creating a new runnable that would run that code but it is somehow still called from the AWT thread. What is the cause of this and how do I get the code to run from the calculation thread?
class AddTribe(dist: Int, maxSpeed: Int, boidMass: Int, color: Color, behavior: Behavior, boidAmount: Int) extends Runnable {
println(Thread.currentThread())
def run() = BoidSimulation.addTribe(dist: Int, maxSpeed: Int, boidMass: Int, color: Color, behavior: Behavior, boidAmount: Int)
}
case ButtonClicked(e) if (e == addButton) => {
new AddTribe(distSlider.value, maxSpdSlider.value / 10, massSlider.value / 10, color, new Flocking(separationSlider.value, alignmentSlider.value, cohesionSlider.value), boidSlider.value).run()
Upvotes: 0
Views: 134
Reputation: 17431
Calling .run()
on a Runnable
directly will just run it on the current thread. The simplest way to run it separately is to create a new Thread
and start()
it:
case ButtonClicked(e) if (e == addButton) =>
new Thread(new AddTribeRunnable(...)).start()
Starting a new thread for each time the button is pressed is somewhat inefficient though; you might prefer to e.g. use a ThreadPoolExecutorService
to manage a pool of threads. Or for a more advanced/high-performance technique you could look at using akka Actors.
Upvotes: 0
Reputation: 42597
The keys things to remember about Swing threads are that:
SwingUtilities.invokeLater()
to add work to the queue for the Swing thread, or use the SwingWorker
helper class.See also the Java tutorials on these topics:
Update:
You can check what thread is running any given bit of code by adding debug statements such as:
println(Thread.currentThread)
or using debugger tools. It helps to give readable names to any threads that you create yourself, using the appropriate constructor.
Upvotes: 3