停止非循环Java线程

时间:2012-02-29 02:29:06

标签: java sql multithreading thread-safety

这可能是我误解了我所读到的内容的一个例子,但是用Java杀死一个线程的所有例子似乎都表明你必须发出一个信号来杀死自己;没有一些严重的风险,你不能从外面杀死它。问题是,如何“礼貌地”要求线程死的所有示例都有某种循环,所以你要做的就是在每次迭代时都看一个标志。

所以,我得到的是一个线程,它只需要一段时间(一系列SQL查询)。我当然可以在每一步之后进行检查,但它们不是在一个循环中,并且我没有一种非常优雅的方式可以解决这个问题。这是我正在做的一个例子:

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();

这是一个例子,我不使用匿名内部类,但它说明我的run()方法不能优雅地停止自己。此外,即使我在每个步骤之后检查,如果特定查询需要很长时间才能运行,则此代码将无法在查询完成之后停止。

此代码适用于GUI应用程序,我真的希望找到一种在不使用Thread.stop()的情况下快速终止线程的好方法。

编辑 - yshavit的答案是一个很大的帮助,因为我不知道Statement.cancel()存在。如果你很好奇,我的特定问题的答案是构建一个更抽象的数据库访问类。该类必须创建一个子线程来执行查询并在其运行时循环,检查每次迭代是否当前线程(而不是子线程)被中断。如果它被中断,它只调用Statement.cancel(),子线程将抛出异常并死掉。并非所有JDBC驱动程序都支持Statement.cancel(),但Oracle 11g支持{。}。

5 个答案:

答案 0 :(得分:4)

找出需要一段时间的内容,然后取消它。如果占用时间最多的是rs.next()循环,则可以执行以下操作:

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

如果需要一段时间的事情是语句,并且您的JDBC驱动程序/服务器支持Statement.cancel,那么您可以将Statement发布到负责调用{{1}的第二个线程} 作为适当的。我不确定,但我认为这会导致来自驱动程序的Statement.cancel,然后您可以以某种方式识别出来自取消,并据此处理。

另外,你应该考虑重构一下。你有三个“运行一个查询,迭代它的结果”的块,可以将它们考虑在一个方法中(然后用它来关闭语句等)。

答案 1 :(得分:1)

中断客户端的线程(即在线程外部运行的代码):

threadInstance.interrupt();

检查代码运行的线程是否已被中断:

Thread.currentThread().isInterrupted()

另一种选择是试着睡觉:

Thread.currentThread().sleep(1)

睡眠的好处是,如果线程被中断,它会抛出异常(即InterruptedException)这就是为什么不忽略这些异常很重要。

您可以使用Thread.currentThread()。isInterrupted()在while循环中添加检查,或者可以在语句之间检查sleep(1)。我不会在循环中睡觉,因为这会降低你的代码速度。这是混合方法可能最好的地方:

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

然后你在run()方法的边界捕获它并停止。

关键是您必须定期检查外部客户端是否已请求您关闭。如果你在一个帖子中有6个语句。在这些语句之间进行检查将允许您的线程退出。

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.");
    }
}

在执行此操作并在循环中执行单个检查之间确实没有区别。

答案 2 :(得分:0)

一旦run()完成,Java线程将自行关闭。如果你在循环中运行run(),那么断开循环,Thread.run将结束,线程将会死亡。如果我没弄错的话你也可以使用return;

答案 3 :(得分:0)

如果您不想从头开始实现自己的thread.kill()机制,可以使用现有的API,在ThreadPoolExecutor内管理您的线程创建,并使用Future.cancel()来杀死正在运行的线程:

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);
... ...

取消方法将根据您的任务运行状态而有所不同,请查看API以获取更多详细信息。

答案 4 :(得分:0)

如果你不想中断线程,唯一的另一个选择是以某种方式杀死/取消长时间运行的查询。如果你可以启动查询异步,你可以等到结果准备就绪,或者你收到一个死信号,但你cannot call jdbc async