Java 条件线程中断

Java 条件线程中断,java,concurrency,interrupt,Java,Concurrency,Interrupt,在我正在编写的一些代码中,有几个地方我可以这样做: public MyThread extends Thread{ boolean finished = false; BlockingQueue<Foo> blockingQueue new LinkedBlockingQueue<Foo>(); public void run(){ while(!finished || (finished && !blocking

在我正在编写的一些代码中,有几个地方我可以这样做:

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();
    }
}
publicMythread扩展线程{
布尔完成=假;
BlockingQueue BlockingQueue new LinkedBlockingQueue();
公开募捐{
而(!finished | |)(finished&!blockingQueue.isEmpty()){
试一试{
Foo f=blockingQueue.take();
插入数据库(f);
}
捕捉(中断异常e){
}
}
}
public void insertThis(Foo f)抛出InterruptedException{
阻塞队列。put(f);
}
公共停车场(){
完成=正确;
这个。中断();
}
}
但是,这会导致问题,因为当线程位于
insertIntoDatabase()
方法中时,它有时会被中断。当我们使用ApacheDerby时,它抛出了一个刺耳的信号(也就是说:“java.sql.SQLException:Derby线程在磁盘I/O操作期间收到了一个中断,请检查您的应用程序以查找中断的来源。”),所有后续的数据库通信都陷入了一片混乱。当线程不在阻塞队列上等待时,是否有任何整洁的方法来保护线程不受中断的影响,或者将中断定位在阻塞队列上


我看到的两个建议或想到的解决方案是,在队列中插入一个“毒药丸”对象以关闭它,并有一个额外的
boolean interruptsWelcome
字段,可以通过
stop()
方法进行检查,但这两个类对我来说都不是特别有吸引力——我要么不得不打乱类的层次结构(
Foo
不是一个简单的类),要么产生大量的同步代码。是否有更整洁的方法?

如果您只想在运行
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){

        }
    }

您可能要做的第一件事是使用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的问题。通过使用SingleThreadExecutor,您可以通过线程限制限制一次上载。如果服务中只有一个线程在运行,那么一次只能运行一个runnable。其他人将排队等待上一个进程完成。

您可以放置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);
    }
}
不要使用这个。那么中断()。它似乎按预期工作,但JDBC驱动程序正在将InterruptedException转换为SQLException,从而绕过异常处理


只需设置finished boolean并等待所有I/O完成。

读取
finished
变量就足够了,但前提是它是
volatile
。如果没有volatile,则无法保证读取线程会看到写入。

如何在
Executor
任务上同步,以便a)一次只上载一个对象,b)当
MyThread
停止时,我们知道不再发生数据库交互?Scott:a)您可以使用
Executors.newSingleThreadExecutor()
来确保顺序执行。b) 从代码中,当MyThread停止时,它不会向执行者提交新任务。上一次提交的任务可能会比MyThread活得更长,但应该可以。从
MyThread.run()
返回之前,您可以调用
ExecutorService.shutdown()
,以确保最后一个sumbite任务完成。您是说Apache代码本身不处理线程中断,并且希望在Apache代码运行时阻止中断吗?因为我认为这是Apache代码中的一个bug,我认为这是一个特性而不是bug。它显示了所有可恢复的迹象-只是当前连接已断开,但我不想在应用程序生命周期即将结束时打开新连接。看起来我再次被发现在不必要的情况下重新发明轮子。当我写
MyThread
时,我觉得自己相当聪明:-D似乎
ExecutorService
是一条路要走
Foo
已经基本上是可运行的了,它们过去都是作为单独的线程运行的。我的一个同事试图在没有条件概念的情况下实现同步器。当我向他展示倒计时是什么时,他脸上的沮丧几乎和我3小时前给出的答案一样好,;)除了submit()不会抛出InterruptedException,正如海报上所说,insertIntoDatabase()也不会抛出它(它给出了不同的异常)OP说Foo是一个复杂的对象,所以包装它可能比扩展它要好。如果我只是将
finished
设置为true,如果线程在
blockingQueue.take()
处被阻塞,并且阻塞队列已经为空,并且可能不会有更多的条目到达,那么它可能永远不会完成。因此,使用interrupt.use
poll(…)
而不是
take()
。在不中断重要操作(如与DB对话)的情况下,可以控制线程的完成。它还为我提供了一个繁忙的等待,并需要代码来处理
poll()
超时的情况。看起来你不能既有蛋糕又有蛋糕。如果您发出中断,而像JDBC驱动程序这样的库决定将其包装到不同的异常中,那么您要么需要处理此异常并在DB中处理回滚,要么必须等待某些操作完成。如果您想保证从阻塞队列中获取的项目已被处理,则不能发出中断。我理解“毒丸”方法,但我不想这样做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(); } }