Conditional thread interrupt

允我心安 提交于 2019-12-11 03:40:00

问题


In some code I'm working on, there are a couple of places where I do something like this:

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

This, however, is causing problems as the thread is sometimes interrupted when it is in the insertIntoDatabase() method. As we're using Apache Derby, it throws a hissy fit (to wit: "java.sql.SQLException: Derby thread received an interrupt during a disk I/O operation, please check your application for the source of the interrupt."), and all subsequent database communication falls down in a nasty mess. Is there any tidy way to protect the thread from an interrupt when it's not waiting on the blocking queue, or alternatively to target the interrupt at the blocking queue?

Two solutions that I have seen suggested or have occurred to me are to insert a "poison pill" object into the queue to shut it down, and to have an extra boolean interruptsWelcome field that could be checked by the stop() method, but neither is particularly attractive to me - I'd either have to mess with the class hierarchy (Foo is not a trivial class) or produce masses of synchronisation code. Is there something neater?


回答1:


The first thing you may want to do is to use an ExecutorService. In which you can use a single thread to submit the foo requests.

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

    }

Your runnable itself can just ignore interrupted exception if you want

class RunnableFoo extends Foo implements Runnable{

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

Edit:

I saw your comment on the other answer and to answer that question in terms of using an ExecutorService. By having a singleThreadExeuctor you are restricting to one upload via thread-confinement. If only one thread is running within the service only one runnable will run at a time. The others will just queue up until the previous finished.




回答2:


If all you want is not to allow thread interruption while running insertIntoDatabase(), factor out call to this method to separate Runnable and submit it to separate Executor:

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

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

        }
    }



回答3:


You could place sentinel value to stop processing. This doesn't require an interrupt or complex checking.

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

Another way might be to use an 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();
   }
}



回答4:


Don't use this.interrupt() then. It seems it works as intended, but the JDBC driver is turning the InterruptedException into an SQLException, thus bypassing your exception handling.

Just set the finished boolean and wait for all I/O to complete.




回答5:


Reading the finished variable should be enough, but only if it is volatile. Without volatile there's no guarantee the reading thread will ever see a write to it.



来源:https://stackoverflow.com/questions/4737870/conditional-thread-interrupt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!