条件线程中断

时间:2011-01-19 16:28:19

标签: java concurrency interrupt

在我正在研究的一些代码中,有几个地方我会做这样的事情:

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线程在磁盘I / O操作期间收到一个中断,请检查您的应用程序是否有中断源。”) ,以及随后的所有数据库通信都陷入了困境。当没有等待阻塞队列,或者在阻塞队列中定位中断时,是否有任何保护线程免受中断的整洁方法?

我见过的两个解决方案建议或已经发生在我的队列中插入一个“毒丸”对象来关闭它,并有一个额外的boolean interruptsWelcome字段,可由{ {1}}方法,但对我来说都不是特别有吸引力 - 我要么必须弄乱类层次结构(stop()不是一个普通的类)或者产生大量的同步代码。有什么更整洁的东西吗?

5 个答案:

答案 0 :(得分:3)

您可能想要做的第一件事是使用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();
        }

    }

如果你想要

,你的runnable本身可以忽略中断的异常
class RunnableFoo extends Foo implements Runnable{

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

编辑:

我看到你对其他答案的评论,并在使用ExecutorService方面回答了这个问题。通过使用singleThreadExeuctor,您可以通过线程限制限制一次上传。如果服务中只运行一个线程,则一次只运行一个runnable。其他人将排队等到上一次结束。

答案 1 :(得分:2)

如果你想要的只是在运行insertIntoDatabase()时不允许线程中断,请将对此方法的调用分解出来以分离Runnable并将其提交给单独的Executor:

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

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

        }
    }

答案 2 :(得分:2)

您可以放置​​sentinel值以停止处理。这不需要中断或复杂的检查。

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

答案 3 :(得分:0)

不要使用this.interrupt()。它似乎按预期工作,但JDBC驱动程序正在将InterruptedException转换为SQLException,从而绕过了异常处理。

只需设置完成的布尔值并等待所有I / O完成。

答案 4 :(得分:0)

阅读finished变量应该足够了,但前提是volatile。没有volatile,就不能保证读取线程会看到它的写入。