2012-02-29 4 views
7

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

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

+0

Возможный дубликат [Как прервать поток быстрым и чистым способом в java?] (Http://stackoverflow.com/questions/94011/how-to-abort-a-thread-in-a-fast -and-clean-way-in-java) –

+0

Вам нужно установить volatile boolean и проверить его или проверить, был ли поток прерван (и прерывать его из другого потока, чтобы заставить его отключиться). –

ответ

4

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

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

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

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

+0

Многие драйверы баз данных не будут потокобезопасными для объектов Statement и ResultSet, хотя спецификация JDBC говорит, что они должны быть. –

+0

Это то, чего я пытался избежать, просто так уродливо смотреть, чтобы добавить эту проверку каждый раз, когда я повторяюсь. Кроме того, это действительно не обойти эту проблему с длинными запросами. ResultSet доступен только после завершения запроса, и, к сожалению, у меня нет возможности улучшить структуру базы данных для улучшения запросов. – monitorjbl

+0

@monitorjbl относительно уродства, на самом деле нет альтернативы. Вот почему я предложил рефакторинг кода - он объединяет уродство. Что касается самого запроса, который занимает много времени, если вы не можете отменить его из-за драйвера, вы не можете многое сделать из своего приложения. – yshavit

0

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

1

Если вы не хотите, чтобы реализовать свой собственный 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 для более подробной информации.

0

прервать поток клиента (то есть код, выполняемый вне резьбы):

threadInstance.interrupt(); 

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

Thread.currentThread().isInterrupted() 

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

Thread.currentThread().sleep(1) 

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

Вы можете добавить проверки внутри ваших циклов while, используя Thread.currentThread(). IsInterrupted(), или вы можете проверить наличие сна (1) между операторами. Я бы не спал в цикле, так как это действительно замедлит ваш код. Это где гибридный подход может быть лучше:

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

Тогда вы поймете, что в рамках забега метода() и остановки.

Суть заключается в том, что вы должны периодически проверять, запрашивал ли внешний клиент выключение. Если в потоке было 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

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

Смежные вопросы