簡體   English   中英

條件線程中斷

[英]Conditional thread 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字段可以通過stop()方法進行檢查,但是都不能對我來說特別有吸引力-我要么不得不弄亂類的層次結構( Foo並不是一個簡單的類),要么產生大量的同步代碼。 有沒有更整潔的東西?

您可能要做的第一件事是使用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,您將只能通過線程約束限制一次上傳。 如果服務中僅運行一個線程,則一次只能運行一個可運行線程。 其他的只會排隊直到上一個完成。

如果您只想在運行insertIntoDatabase()時不允許線程中斷,請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){

        }
    }

您可以放置​​哨兵值以停止處理。 這不需要中斷或復雜的檢查。

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

然后不要使用this.interrupt()。 看來它按預期工作,但是JDBC驅動程序將InterruptedException轉換為SQLException,從而繞過了異常處理。

只需設置完成的布爾值,然后等待所有I / O完成即可。

讀取finished變量應該足夠了,但前提是它是volatile 如果沒有volatile,則不能保證讀取線程會看到對其進行寫操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM