停止非循环Java线程

这可能是我误解我所读到的内容的一个例子,但是用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()并且子线程将抛出exception并死掉。 并非所有JDBC驱动程序都支持Statement.cancel() ,但Oracle 11g支持。

找出需要一段时间的内容,然后取消它。 如果花费最多时间的东西是rs.next()循环,你可以这样做:

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

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

另外,你应该考虑重构一下。 您有三个“运行查询,迭代其结果”的块,可以将其纳入方法(然后处理关闭语句等)。

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

如果您不想从头开始实现自己的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以获取更多详细信息。

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

 threadInstance.interrupt(); 

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

 Thread.currentThread().isInterrupted() 

另一种选择是尝试睡觉:

 Thread.currentThread().sleep(1) 

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

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

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

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

关键是您必须定期检查外部客户端是否已请求您关闭。 如果你在一个post中有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."); } } 

这样做和在循环中进行单一检查之间确实没有区别。

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