Остановка не зацикливающегося потока Java - PullRequest
7 голосов
/ 29 февраля 2012

Это может быть случай, когда я просто неправильно понял то, что я прочитал, но все примеры уничтожения потока в 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 () не может элегантно остановить себя. Более того, даже после каждого шага я проверяю, выполняется ли определенный запрос очень долго, этот код не сможет остановиться до тех пор, пока запрос не будет завершен.

Этот код предназначен для приложения с графическим интерфейсом, и я действительно хотел бы найти хороший способ быстро завершить поток без использования Thread.stop ().

РЕДАКТИРОВАТЬ - ответ ишавита мне очень помог, так как я не знал, что Statement.cancel() существует. Если вам интересно, ответом на мою конкретную проблему было создание более абстрактного класса доступа к базе данных. Класс должен был создать дочерний поток для выполнения запроса и цикла во время его выполнения, проверяя на каждой итерации, прерывается ли текущий поток (не дочерний). Если он прерывается, он просто вызывает Statement.cancel (), и дочерний поток генерирует исключение и умирает. Не все драйверы JDBC поддерживают Statement.cancel(), но Oracle 11g поддерживает.

Ответы [ 5 ]

4 голосов
/ 29 февраля 2012

Узнайте, что занимает некоторое время, и отмените это. Если больше всего времени занимает цикл rs.next(), вы можете сделать:

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

Если что-то, что требует времени, - это операторы, а ваш драйвер / сервер JDBC поддерживает Statement.cancel, то вы можете опубликовать свой Statement во втором потоке, который отвечает за вызов Statement.cancel в зависимости от ситуации. Я не уверен, но я думаю, что это приведет к SQLException от драйвера, который затем можно будет как-то определить как пришедший из отмены, и обработать соответствующим образом.

Кроме того, вам следует немного подумать о рефакторинге. У вас есть три блока «выполнить запрос, выполнить итерации по его результатам», которые можно включить в метод (который затем позаботится о закрытии оператора и т. Д.).

1 голос
/ 29 февраля 2012

Чтобы прервать поток клиента (т. Е. Код, выполняющийся вне потока):

threadInstance.interrupt();

Чтобы проверить, был ли прерван поток, в котором выполняется ваш код:

Thread.currentThread().isInterrupted()

Еще один вариант - попытаться уснуть:

Thread.currentThread().sleep(1)

Что хорошо в сне, так это то, что оно будет выдавать исключение, если поток был прерван (т.е. InterruptedException). Поэтому важно не игнорировать эти исключения.

Вы можете добавить проверки внутри ваших циклов while, используя Thread.currentThread (). IsInterrupted (), или вы можете проверить на наличие сна (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.");
    }
}

На самом деле нет никакой разницы между выполнением этого и помещением одной проверки в цикл.

0 голосов
/ 29 февраля 2012

Если вы не хотите прерывать поток, единственная другая возможность - как-то убить / отменить длительный запрос. Если бы вы могли запустить асинхронный запрос, вы могли бы просто подождать, пока результаты не будут готовы, или вы получите сигнал о смерти, но вы не можете вызвать jdbc async .

0 голосов
/ 29 февраля 2012

Если вы не хотите реализовывать свой собственный механизм thread.kill () с нуля, вы можете использовать существующий API, управлять созданием потока в ThreadPoolExecutor и использовать Future. отмена () чтобы убить работающий поток:

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.

0 голосов
/ 29 февраля 2012

Поток Java закроется сам по завершении run ().Если вы выполнили run () в цикле, вырвитесь из цикла, Thread.run завершится и поток умрет.Вы также можете использовать return;, если я не ошибаюсь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...