【问题标题】:Java - Stop a long running thread which accesses databaseJava - 停止访问数据库的长时间运行的线程
【发布时间】:2013-05-16 13:59:27
【问题描述】:

我启动了几个线程,但我没有通过信号或其他方式停止它们的引用。 例如,我不能将像 running=false 这样的变量传递给那些线程,因为我没有它们的引用,但有它们的名称。

我使用的是ThreadGroup,我总是有它的参考。所以我可以做这样的事情。好像不行啊。

    Thread[] threads = new Thread[threadGroup.activeCount()];
    int count = threadGroup.enumerate(threads);
    for(int i = 0; i < count; i++){
        threads[i].interrupt();
    }

这是我的主题的一个示例。

    public void run{

         try{
             //myDAO.getRecords();
             //this takes 30seconds to 60
             //returns about 3 millions of records

         }catch(Exception e){
             //log
         }
    }

当这个线程正在执行时,我想在中间停止它。 无论如何,数据库查询正在运行,但我想停止获取结果。

即使我打电话给interrupt(),我仍然得到了结果。 有没有其他方法可以做到这一点,或者我做错了什么?最终的任务是从 Java 中取消一个长时间运行的 sql 查询。

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    为等待查询输出的线程调用interrupt 没有任何影响,因为大多数 JDBC 驱动程序都不受该状态的影响。它仍将保持阻塞状态,查询将继续执行。

    调用cancel 将终止连接和数据库中执行查询的线程。偶尔它还可以,但它也会终止连接。这会造成严重的问题,很快就会成为瓶颈。

    另一种可行的解决方案是获取执行过程/查询的线程的ID(在数据库端)并调用:

    KILL QUERY id;
    

    KILL QUERY 终止连接当前正在执行的语句,但保持连接本身不变。

    要知道ID,就在程序中,将第一行设为:SELECT CONNECTION_ID();。这个 Id 可以用来终止它。

    【讨论】:

    【解决方案2】:

    如果您的 DAO 正在使用 JDBC,并且您想要停止正在进行的查询,您可以在语句上让另一个线程调用 cancel

    无效取消() 抛出 SQLException

    Cancels this Statement object if both the DBMS and driver support aborting 
    an SQL statement. This method can be used by one thread to
    cancel a statement that is being executed by another thread.
    
    Throws:
        SQLException - if a database access error occurs or this method is 
        called on a closed Statement 
        SQLFeatureNotSupportedException - if the JDBC driver does not support
        this method
    

    您可能让 run 方法将 DAO 调用委托给另一个线程,并让它监听中断并调用取消。

    这里是a post where somebody's using a Spring JdbcTemplate to cancel a query。所以它适用于外面的人(使用 MySQL)。

    还有see this answer describing how queries get canceled in Oracle

    【讨论】:

    • 我对此一无所知,但我会调查一下。
    • 当心。 cancel 也会终止连接。如果你经常使用它,那么它会降低性能
    • @Jatin:让查询在不需要时运行也对性能不利。但意识到连接将丢失是一件好事。
    • @NathanHughes 更好的办法是使用Kill_query id(如果你完全知道id)。这不会终止连接,只会指示 db 线程停止执行查询(在下面的答案中有更多提及)。我遇到了这种情况,这看起来是最好的方法
    【解决方案3】:

    我仍然得到调用中断()的结果事件。有没有其他方法可以做到这一点,或者我做错了什么?

    当您的线程被中断时,您需要检查run() 您的线程是否通过isInterrupted() 条件。

    我认为interrupt 是实现这一目标的最佳方式,因为an interrupt will unblock some blocking IO 和同步请求。定制解决方案无法做到这一点。

    【讨论】:

    • 我现在正在做,但我认为可能有更好的方法。
    • 我对此完全不确定,但我猜interrupt 不会解除对数据库连接的阻塞。我知道它不会解除对套接字连接的阻塞。
    【解决方案4】:

    您的线程代码需要捕获InterruptedException,并在您的线程上设置中断标志。请参阅this JavaSpecialist newsletter 了解更多信息。

      try {
        // ... 
      } 
      catch (InterruptedException ex) {
        Thread.currentThread().interrupt(); // very important
        break;
      }
    

    要中断的线程必须不受计算限制。也就是说,它应该正在执行网络 IO、睡眠等,以便捕获并响应InterruptedExceptionwhile(1) {} 这样的循环不会被中断。

    【讨论】:

    • 它给了我这个Unreachable catch block for InterruptedException. This exception is never thrown from the try statement body 编译错误。
    • 我猜你的 DAO 捕获了异常(并且很可能无法正确处理这种情况)
    • IntrruptedException 可以被sleep() 调用。
    • 没错。 InterruptedException 实际上只中断了少量的事情(比如睡眠)。一般情况下,需要使用isInterrupted()方法来检测中断。
    【解决方案5】:

    下面的代码会无限期中断正在运行的线程,中断后线程会强制停止。

             @Override public void run() 
               {   // infinite loop to process
    
                       while(true) // your variable 
                       {                              
                          // We've been interrupted: no more processing.
                          if(Thread.currentThread().isInterrupted()){
                             return;
                           }
                       }
    
                }
             }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-12
      • 1970-01-01
      • 1970-01-01
      • 2014-02-11
      • 2018-06-03
      • 1970-01-01
      • 2012-03-15
      • 1970-01-01
      相关资源
      最近更新 更多