2009-09-16 66 views
4

我在試圖瞭解如何確保特定操作在一定時間內完成。對於Java新的util.concurrent庫看起來像一個簡單的工作。但是,此任務聲明與數據庫的連接,我想確保它在超時後正確釋放該連接。關於FutureTask,最後和Java中的TimeoutExceptions

這樣調用服務:

int resultCount = -1; 
ExecutorService executor = null; 
try { 
executor = Executors.newSingleThreadExecutor(); 
FutureTask<Integer> task = new CopyTask<Integer>(); 
executor.execute(task); 
try { 
    resultCount = task.get(2, TimeUnit.MINUTES); 
} catch (Exception e) { 
    LOGGER.fatal("Migrate Events job crashed.", e); 
    task.cancel(true); 
    return; 
} 
} finally { 
if (executor != null) { 
executor.shutdown(); 
} 

本身只是一個wrapps可調用的任務,這裏是調用方法:

@Override 
public Integer call() throws Exception { 
Session session = null; 
try { 
    session = getSession(); 
    ... execute sql against sesssion ... 
    } 
} finally { 
    if (session != null) { 
    session.release(); 
    } 
} 
} 

所以,我對於那些誰找到了這個問題很遠,是:是否在由於TimeoutException導致任務失敗的情況下調用session.release()方法?我假設它不是,但我希望被證明是錯誤的。

感謝

編輯:我遇到的問題是,偶爾有問題的SQL是沒有完成,由於奇怪的分貝問題。所以,我想要做的只是關閉連接,讓數據庫回滾事務,休息一下,稍後重新嘗試。所以我把這個(......)對待,就好像它殺死了那個人一樣。那是錯的嗎?

回答

11

當你調用task.get()以超時,即超時只適用於試圖獲得的結果(在當前線程),而不是計算本身(在工作線程)。因此你的問題在這裏;如果一個工作線程進入某種永遠不會返回的狀態,那麼超時只會確保您的輪詢代碼將繼續運行,但不會影響工作人員。

您對task.cancel(true)在catch塊調用是我最初要建議,這是良好的編碼習慣。不幸的是,這隻在線程上設置了一個標誌,可以/應該通過運行良好的長時間運行的可取消任務來檢查它,但是它不會對其他線程採取任何直接行動。如果SQL執行方法沒有聲明它們拋出InterruptedException,那麼它們不會檢查該標誌,並且不會通過典型的Java機制中斷。

真的這一切歸結爲一個事實,即如果它的運行時間過長在工作線程的代碼必須支持停止本身的一些機制。支持標準的中斷機制是這樣做的一種方式;間歇性地檢查一些布爾標誌,或其他定製的替代品,也可以起作用。但是,沒有保證的方法可以使另一個線程返回(短於Thread.stop,這已棄用for good reason)。您需要與運行代碼協調,以便通知它停止。

在這種特殊情況下,我預計可能會在數據庫連接上設置一些參數,以便SQL調用在給定的時間段後超時,這意味着控制返回到您的Java代碼(可能有一些例外)所以finally塊被調用。如果沒有,也就是說沒有辦法讓數據庫調用(例如PreparedStatement.execute())在一段預定時間後返回控制權,那麼你需要在Callable中產生一個額外的線程,它可以監視超時並強制關閉連接/會話if它到期。這不是很好,但如果你可以得到SQL調用來協作,你的代碼將會更加清潔。

(具有諷刺意味所以儘管你提供的代碼量好來支持這個問題,真正重要的部分是你刪節位:「... execute sql against sesssion ...」 :-))

+0

謝謝這是一個很好的評論。我沒有考慮利用連接超時的事件。 謝謝 – 2009-09-25 17:36:08

2

你不能從外部中斷一個線程,所以超時將不會影響JDBC層的代碼(甚至可能在JNI-land的某個地方)。推測最終SQL工作將結束並且會話結束。 release()會發生,但是可能會在超時結束後很長時間。

0

你舉的例子說:

copyRecords.cancel(true); 

我認爲這是爲了說:

task.cancel(true); 

你finally塊將被稱爲假定try塊的內容是中斷的。有些操作(比如wait()),有些操作不是(比如InputStream#read())。這完全取決於任務被中斷時代碼阻塞的操作。

2

finally塊最終將執行。

當任務需要更長的時間,然後2分鐘,一個TimeoutException被拋出,但實際的線程繼續執行它的工作,並最終它會調用finally塊。即使你取消任務並強制中斷,finally塊也會被調用。

這裏是基於代碼中的小例子。您可以測試這些情況:

public static void main(String[] args) { 
    int resultCount = -1; 
    ExecutorService executor = null; 
    try { 
     executor = Executors.newSingleThreadExecutor(); 
     FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() { 
      @Override 
      public Integer call() throws Exception { 
       try { 
        Thread.sleep(10000); 
        return 1; 
       } finally { 
        System.out.println("FINALLY CALLED!!!"); 
       } 
      } 
     }); 
     executor.execute(task); 
     try { 
      resultCount = task.get(1000, TimeUnit.MILLISECONDS); 
     } catch (Exception e) { 
      System.out.println("Migrate Events job crashed: " + e.getMessage()); 
      task.cancel(true); 
      return; 
     } 
    } finally { 
     if (executor != null) { 
      executor.shutdown(); 
     } 
    } 
} 
+0

這段代碼的問題是,我的模擬:Thread.sleep,可能永遠不會完成。但我仍然想看到'最後叫' – 2009-09-17 16:43:02