Условное прерывание потока - PullRequest
2 голосов
/ 19 января 2011

В некотором коде, над которым я работаю, есть пара мест, где я делаю что-то вроде этого:

public MyThread extends Thread{
    boolean finished = false;
    BlockingQueue<Foo> blockingQueue new LinkedBlockingQueue<Foo>();

    public void run(){
        while(!finished || (finished && !blockingQueue.isEmpty())){
            try{
                Foo f = blockingQueue.take();

                insertIntoDatabase(f);
            }
            catch(InterruptedException e){

            }
        }
    }

    public void insertThis(Foo f) throws InterruptedException{
        blockingQueue.put(f);
    }

    public void stop(){
        finished = true;
        this.interrupt();
    }
}

Это, однако, вызывает проблемы, так как поток иногда прерывается, когда онв методе insertIntoDatabase().Поскольку мы используем Apache Derby, он выбрасывает шипение (например: java.sql.SQLException: поток Derby получил прерывание во время операции ввода-вывода на диске, пожалуйста, проверьте ваше приложение на предмет источника прерывания. ")и все последующее взаимодействие с базой данных падает в неприятном беспорядке.Есть ли какой-нибудь аккуратный способ защитить поток от прерывания, когда он не ожидает в очереди блокировки, или в качестве альтернативы нацелить прерывание в очереди блокировки?

Два решения, которые я видел, предложили или мне пришло в головувставлять объект «ядовитую таблетку» в очередь, чтобы закрыть его, и иметь дополнительное поле boolean interruptsWelcome, которое можно проверить методом stop(), но ни одно из них не особенно привлекательно для меня - я бы тожесвязываться с иерархией классов (Foo не является тривиальным классом) или создавать массы кода синхронизации.Есть ли что-нибудь аккуратнее?

Ответы [ 5 ]

3 голосов
/ 19 января 2011

Первое, что вы можете сделать, это использовать ExecutorService.В котором вы можете использовать один поток для отправки запросов foo.

    class FooRunner{

     ExecutorService service = Executors.newSingleThreadExecutor();

     //where RunnableFoo extends Foo and implements Runnable
        public void insertThis(RunnableFoo f) throws InterruptedException{ Run
            service.submit(f);
        }
        public void stop(){
            service.shutdown();
        }

    }

Сам ваш исполняемый файл может просто игнорировать прерванное исключение, если вы хотите

class RunnableFoo extends Foo implements Runnable{

 public void run(){
     try{
         insertIntoDatabase(this);
     }catch(InterruptedException ex){
       ex.printStackTrace();
     }
 }
}

Редактировать:

Я видел ваш комментарий к другому ответу и ответил на этот вопрос с точки зрения использования ExecutorService.Имея singleThreadExeuctor, вы ограничиваетесь одной загрузкой через ограничение потока.Если в службе запущен только один поток, одновременно будет работать только один работающий.Остальные просто будут стоять в очереди, пока не закончится предыдущий.

2 голосов
/ 19 января 2011

Вы можете поместить значение часового, чтобы остановить обработку.Это не требует прерывания или сложной проверки.

public class MyThread extends Thread{
    static final Foo STOP = new Foo();
    final BlockingQueue<Foo> queue = new LinkedBlockingQueue<Foo>();

    public void run(){
        try{
           Foo f = queue.take();
           while(f != STOP) {
              insertIntoDatabase(f);
              f = queue.take();
           }
        } catch(InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void insertThis(Foo f) {
        queue.add(f); // never fills up.
    }

    public void stop() {
        queue.add(STOP);
    }
}

Другим способом может быть использование ExecutorService.

public class DatabaseWriter {
   final ExecutorService es = Executors.newSingleThreadExecutor();

   public void insertThis(final Foo f) {
       es.submit(new Runnable() {
           public void run() {
              insertIntoDatabase(f);
           }
       });
   }

   public void stop() {
       es.shutdown();
   }
}
2 голосов
/ 19 января 2011

Если все, что вам нужно, это не разрешать прерывание потока во время выполнения insertIntoDatabase(), исключите вызов этого метода для разделения Runnable и отправьте его отдельному исполнителю:

    while(!finished || (finished && !blockingQueue.isEmpty())){
        try{
            final Foo f = blockingQueue.take();

            executor.submit(new Runnable() {
                public void run() {
                    insertIntoDatabase(f);
                }
            });
        }
        catch(InterruptedException e){

        }
    }
0 голосов
/ 20 января 2011

Чтение переменной finished должно быть достаточно, но только если это volatile. Без volatile нет никакой гарантии, что читающая ветка когда-либо увидит запись в нее.

0 голосов
/ 19 января 2011

Не используйте this.interrupt () тогда.Кажется, что он работает как задумано, но драйвер JDBC превращает InterruptedException в SQLException, таким образом обходя обработку исключений.

Просто установите готовый логический тип и дождитесь завершения всех операций ввода-вывода.

...