How can I abruptly and mercilessly interrupt a task in Java?

I have programmed sudoku in Java for homework and am currently trying to figure out the problems that it might run into to make it better. I have created several thousand sudoku nets using the David Bau sudoku generator , and now I am running my program against them.

The problem is that although most of them end in very reasonable times, some of them turn out to be problematic and make my search algorithm crazy until I exit the heap. Therefore, I thought that I should leave the work of deciding on a secondary thread and start it with a timeout. Right now I am using the thread pool of one thread (in the form of an ExecutorService ), and I am sending it Callable . Then I try to get the timeout value:

 Callable<Long> solveAndReturnTime = new Callable<Long>() { /* snip */ }; Future<Long> time = executor.submit(solveAndReturnTime); try { long result = time.get(10, TimeUnit.SECONDS); System.out.printf("%d millis\n", result); } catch (TimeoutException e) { System.err.println("timed out"); time.cancel(true); } 

My problem is that apparently one doesnโ€™t just cancel Future in Java. Future<T>.cancel(boolean) does not seem to interrupt the task immediately. Because of this, the pool is stuck in the transfer of an undying task and the subsequent timeout of attempts, because they will never have a chance to start.

Adding more threads to the pool is not an option, because I run on limited cores and, if there are too many tasks, work hard, the legitimate ones will be unjustly slowed down. I also do not want the overhead to be frequently checked to see if the task was aborted from my main algorithm.

How can I suddenly, mercilessly and brutally end a task? I am open to anything that will allow me to restore the main thread.

EDIT My algorithm is completely consistent, does not use a global object, and does not contain a lock. As far as I can tell, nothing will go wrong if the task is canceled at a random moment; and even if so, this is not production code. I am ready to go through a dangerous and treacherous walk for this.

+4
source share
3 answers

I believe that my business was โ€œspecialโ€ to use Thread.stop , so here is my solution for people who believe that their case is also enough. (I would very carefully use this somewhere, it could make a difference.)

As usual, everyone points out that there is no clean way to stop a task without checking this task if it should stop. I created a class that implements Runnable to execute in such a way that it is not dramatic if it was killed. The result field ( milliseconds ) is AtomicLong , because records on long regular variables are not guaranteed to be atomic.

 class SolveTimer implements Runnable { private String buildData; private AtomicLong milliseconds = new AtomicLong(-1); public SolveTimer(String buildData) { assert buildData != null; this.buildData = buildData; } public void run() { long time = System.currentTimeMillis(); // create the grid, solve the grid milliseconds.set(System.currentTimeMillis() - time); } public long getDuration() throws ContradictionException { return milliseconds.get(); } } 

My code creates a thread at each iteration and starts SolveTimer . He then tries to enter within 10 seconds. After join returns, the main thread calls getDuration in the start timer; if it returns -1, then the task takes too much time and the thread is destroyed.

 SolveTimer timer = new SolveTimer(buildData); Thread worker = new Thread(timer); worker.start(); worker.join(10000); long result = timer.getDuration(); if (result == -1) { System.err.println("Unable to solve"); worker.stop(); } 

It should be noted that this makes it difficult to debug workflows: when a thread is debugged by the debugger, it can still be killed by Thread.stop() . On my machine, this writes a short ThreadDeath error ThreadDeath in the console and causes the Java process to crash.

There is a possible race condition when the workflow terminates exactly (or right after) getDuration is called, and because of this, the result will be -1 , even if the task really succeeded. However, there is something I can live with: 10 seconds is already too long, so at this moment I donโ€™t care if it is almost good enough.

+1
source

As in any other language, methods for graciously stopping the flow are outdated or not recommended. Because such methods can cause deadlocks (a terminated thread does not release the locks it holds).

The correct solution to the problem has an additional check for Thread.currentThread ().isInterrupted () at each iteration of the main loop in you Callable. Therefore, when the flow is interrupted, he sees this and gracefully closes.

And since you are running the code in another thread, it will not be difficult for you to change it.

+5
source

In addition to the answer, which is correct, you should be aware that doing this work in a thread will not protect your application from running out of memory through OOM. If your worker thread consumes the entire heap, the main thread may die too.

+2
source

All Articles