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;
}
}
}
}