In my game I'm going to implement a function that changes the value of a variable linearly for 5 seconds from 100 to 0. The function has to be launched by the player touching a button.
First, I wanted to do it just by a for
loop with TimeUnit.MILLISECONDS.sleep()
and the value decrementation inside it:
for (int i = 0; i <= 100; i++) {
decrementBatteryLevel();
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
But I quickly found out that this will cause my entire game to stop for 5 seconds, which is not what I want.
So the first thing I actually tried was creating a thread running the for
loop:
private Thread useBattery = new Thread(() -> {
for (int i = 0; i <= 100; i++) {
decrementBatteryLevel();
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
And calling it when necessary:
useBattery.start();
This worked, but only for the first launch. The second launch of the useBattery
Thread resulted throwing java.lang.IllegalThreadStateException
by the game.
I understood that I can't start a thread that is already alive, so I tried doing this:
if (useBattery.isAlive()) {
useBattery.interrupt();
}
useBattery.start();
But I ended up with java.lang.IllegalThreadStateException
being thrown again. /* Is a Thread a single-use product...? */
The final thing I tried and that actually worked was creating a new Thread every time I needed. So here's what I currently have:
new Thread(() -> {
for (int i = 0; i <= 100; i++) {
decrementBatteryLevel();
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
The result of that is satisfying, but I'm afraid that doing so, I quickly fill up RAM with unnecessary, "used up" Threads. Is there any way to do it better?
Thank you.
Use an ExecutorService
to launch tasks.
Executors.newSingleThreadExecutor();
You can then submit
your Runnable
or Callable
to get a hold of a corresponding Future
.
final Future<?> submittedResult = executorService.submit(() -> { /* Your code */ });
When launching a new task instance, simply cancel
and submit
submittedResult.cancel(true);
executorService.submit(() -> { /* Your code */ });
I think you can expand my answer for your specific usecase.
Also, see https://techblog.bozho.net/interrupting-executor-tasks/