Stopping a non-looping Java thread

╄→尐↘猪︶ㄣ 提交于 2019-12-21 04:29:25

问题


This may be a case of me just misunderstanding what I've read, but all the examples for killing a thread in Java seem to indicate that you have to signal a thread to kill itself; you can't kill it from the outside without some serious risks. The problem is, all the examples of how to "politely" ask a thread to die have some kind of looping so all you have to do is watch a flag on every iteration.

So, what I've got is a thread that does something that just takes a while (a series of SQL queries). It's certainly possible for me to just have a check after each step, but they aren't in a loop and there isn't a very elegant way that I'm aware of to get around that. Here's an example of what I'm doing:

new Thread(new Runnable(){
    public void run(){
        //query 1
        Connection conn = db.getConnection();
        Statement s = conn.createStatement();
        ResultSet rs = s.executeQuery("SELECT ...");
        while(rs.next()){
            //do stuff
        }

        //query 2
        rs = s.executeQuery("SELECT ...");
        while(rs.next()){
            //do stuff
        }

        //query 3
        rs = s.executeQuery("SELECT ...");
        while(rs.next()){
            //do stuff
        }
    }
}).start();

This is an example, I don't use anonymous inner classes but it illustrates that my run() method can't elegantly stop itself. Futhermore, even I check after each step, if a particular query takes a very long time to run, this code would not be able to stop until after the query was complete.

This code is for a GUI application, and I would really like to find a good way to kill a thread quickly without using Thread.stop().

EDIT - yshavit's answer was a big help, as I wasn't aware that Statement.cancel() existed. If you're curious, the answer to my particular problem was to build out a more abstracted database access class. The class had to create a child thread to execute the query and loop while it was running, checking on every iteration if the current thread (not the child) was interrupted. If it does get interrupted, it just calls Statement.cancel() and the child thread will throw an exception and die. Not all JDBC drivers support Statement.cancel(), but Oracle 11g does.


回答1:


Find out what takes a while, and cancel it. If the thing that takes the most time are the rs.next() loops, you can do:

while(rs.next()){
    if (myVolatileBooleanSaysToStop) {
        return; // or whatever
    }
    //do stuff
}

If the thing that takes a while are the statements, and your JDBC driver/server support Statement.cancel, then you can publish your Statement to a second thread which is responsible for calling Statement.cancel as appropriate. I'm not sure, but I think that'll result in a SQLException from the driver, which you can then somehow identify as having come from the cancellation, and handle accordingly.

Also, you should consider refactoring a bit. You have three chunks of "run a query, iterate over its results" that could be factored into a method (which would then take care of closing the statement, etc).




回答2:


To interrupt a thread the client (ie code running outside of the thread):

threadInstance.interrupt();

To check to see if the thread your code is running on has been interrupted:

Thread.currentThread().isInterrupted()

Another option is to try and sleep:

Thread.currentThread().sleep(1)

What's nice about sleeping is it will throw an exception if the thread has been interrupted (ie InterruptedException) That's why not ignoring those exceptions is important.

You could add checks inside your while loops using Thread.currentThread().isInterrupted() or you could check for a sleep(1) between statements. I wouldn't sleep in the loop as that will really slow your code down. This is where a hybrid approach might be best:

if( Thread.currentThread().isInterrupted() ) throw new InterruptedException();

Then you catch that at the bounds of your run() method and stop.

The point is you have to periodically check to see if an outside client has requested you shutdown. If you had 6 statements in a thread. Putting checks in between those statements would allow your thread to quit.

public run() {
    try {
       doSomething();
       if( Thread.currentInstance().isInterrupted() ) throw new InterruptException();
       doNextSomething();
       if( Thread.currentInstance().isInterrupted() ) throw new InterruptException();
       doSomeMoreThings();
       if( Thread.currentInstance().isInterrupted() ) throw new InterruptException();
       doYetMoreThings();
    } catch( InterruptedException e ) {
       System.out.println("Duff man going down.");
    }
}

There's really no difference between doing that and putting a single check in a loop.




回答3:


A Java thread will close itself once run() finishes. If you have run() in a loop, break out of the loop, Thread.run will end, and the thread will die. You can also use return; if I'm not mistaken.




回答4:


If you don't want to implement your own thread.kill() mechanism from the scratch, you can use existing API, Manage your thread creation within a ThreadPoolExecutor, and use Future.cancel() to kill the running thread:

ThreadPoolExecutor threadPoolExecutor = Executors.newSingleThreadExecutor();
Runnable longRunningTask = new Runnable();

// submit task to threadpool:
Future longRunningTaskFuture = threadPoolExecutor.submit(longRunningTask);

... ...
// At some point in the future, if you want to kill the task:
longRunningTaskFuture.cancel(true);
... ...

Cancel method will behaviour differently based on your task running state, check the API for more details.




回答5:


If you don't want to interrupt the thread the only other option is to somehow kill/cancel the long running query. If you could start the query async you could just wait until the results are ready or you receive a signal to die but you cannot call jdbc async.



来源:https://stackoverflow.com/questions/9492777/stopping-a-non-looping-java-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!