Java - остановить длинный поток, который обращается к базе данных

Я начинаю пару потоков, но у меня нет ссылок на остановку с помощью сигнализации или чего-то еще. Например, я не могу передать переменную типа 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(). Есть ли другие способы сделать это ИЛИ я сделал что-то неправильно? В конечном итоге задача состоит в том, чтобы отменить длинный SQL-запрос с Java.

Ответ 1

Вызов interrupt для потока, который, как ожидается, ожидает выхода запроса, не влияет, поскольку большинство драйверов JDBC невосприимчивы к статусу. Он все равно останется заблокированным и запрос будет продолжать выполнять.

Вызов cancel приведет к удалению соединения и потока, выполняющего запрос в базе данных. Время от времени это нормально, но также убивает соединение. Это может создать серьезные проблемы и вскоре станет узким местом.

Альтернативой, но рабочим решением было бы получить ID потока, выполняющего процедуру/запрос (на стороне базы данных), и вызвать:

KILL QUERY id;

KILL QUERY завершает утверждение о том, что соединение в настоящее время выполняется, но оставляет соединение невредимым.

Чтобы узнать идентификатор, в правой части процедуры, введите первую строку как: SELECT CONNECTION_ID();. Этот идентификатор можно использовать для его завершения.

Ответ 2

Если ваш DAO использует JDBC и вы хотите остановить выполняемый запрос, вы можете вызвать другой поток cancel по заявлению:

void 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

У вас может быть метод выполнения делегирования вызова DAO другому потоку, и он будет прослушивать прерывание и отменять вызов.

Здесь сообщение, в котором кто-то использует Spring JdbcTemplate для отмены запроса. Поэтому он работает для кого-то там (используя MySQL).

Также см. этот ответ, описывающий отмену запросов в Oracle.

Ответ 3

Тем не менее, я получаю событие результатов, которое я вызываю interrupt(). Есть ли другие способы сделать это ИЛИ я сделал что-то неправильно?

Когда ваш поток был прерван, вам нужно проверить run(), прошел ли ваш поток isInterrupted().

Я думаю, что interrupt - лучший способ добиться этого, потому что an interrupt will unblock some blocking IO и запросы синхронизации. Решение на заказ не может этого сделать.

Ответ 4

Код вашего потока должен поймать InterruptedException и установить прерванный флаг в вашем потоке. Для получения дополнительной информации см. этот информационный бюллетень JavaSpecialist.

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

Поток для прерывания не должен быть связан с вычислением. То есть, он должен выполнять сетевое IO, спать и т.д., Чтобы поймать и ответить на InterruptedException. Цикл, такой как while(1) {}, не будет прерываться.

Ответ 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;
                       }
                   }

            }
         }