Lately I was working on my code which automated many tasks, for example finding path to certain target (by A* algorithm) and moving accordingly through all found nodes in 2D map. I was trying to implement GUI in Swing so I could control the behavior of my code. I created simple snippet:
@Component
public class BotGUI {
private boolean isRunning = true;
@Autowired
private BotInit botInit;
private Thread botThread;
public void stopBot() {
if (this.botThread != null) {
botThread.interrupt();
}
}
public boolean isRunning() {
return this.isRunning;
}
public BotGUI() {
}
public void startGUI() {
this.botThread = new Thread(botInit);
botThread.start();
JFrame jFrame = new JFrame("MagBot Control Panel");
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(300, 100);
JButton toggleButton = new JButton("Stop Bot");
toggleButton.setBackground(Color.lightGray);
toggleButton.setForeground(Color.blue);
toggleButton.setFont(new Font("Arial", Font.BOLD, 16));
Border lineBorder = BorderFactory.createLineBorder(Color.darkGray);
toggleButton.setBorder(lineBorder);
toggleButton.setMargin(new Insets(5, 15, 5, 15));
toggleButton.setOpaque(true);
toggleButton.setContentAreaFilled(false);
toggleButton.addActionListener(e -> {
if (this.isRunning()) {
this.stopBot();
toggleButton.setText("Start Bot");
} else {
this.botThread = new Thread(botInit);
botThread.start();
toggleButton.setText("Stop Bot");
}
this.isRunning = !isRunning;
});
jFrame.getContentPane().add(toggleButton, BorderLayout.CENTER);
jFrame.setVisible(true);
}
}
This snippet of code tries to interrupt a thread which is running some task. But then a problem appears - to handle interruption correctly, I would have to implement constant isInterrupted() checks or rely on catching InterruptedException and propagating it higher and higher. I have many levels of abstraction in my code and I have problem with handling such a task - I've tried spamming button to stop/start my program and despite having interruption checks in key places in code, it doesn't help. I could have interrupted thread within some for loop which isn't aware of thread interruption. How to handle such problem?
How can I interrupt thread without putting try catch blocks everywhere in code? It doesn't look clean and I doubt it is a good practice.
Example of my code:
while (!Thread.currentThread().isInterrupted()) {
bot.processWalkAndAttack();
TimeUnit.MILLISECONDS.sleep(2000);
}
} catch (InterruptedException var3) {
log.error("Bot stopped.");
}
public void processWalkAndAttack() {
try {
List<Monster> monsters = utilityFunction.convertToMonsters();
Monster closestMonster = utilityFunction.findClosestMonster(monsters);
moveModule.moveToTarget(closestMonster.getPosition());
utilityFunction.attackMob();
} catch (InterruptedException e) {
log.error("Interrupted!");
Thread.currentThread().interrupt();
}
}
I don't have interruption checks in every function call because my code is huge and such checks don't seem to be efficient. So there is a chance that some thread won't be stopped and code will run despite being interrupted because in some for loop or some utility method, we didn't declare scenario when thread is interrupted.
You are right.. it would be nice if a piece of code running in a Thread could be "regular" Java code, which is not thread-aware, and that code could be interrupted from outside when needed. But, it doesn't look like there is a good way to do this.
Few suggestions: