2008-12-23 167 views
13

我有一個程序,不斷輪詢數據庫的某些字段的值的變化。它在後臺運行,目前使用一段時間(true)和一個sleep()方法來設置時間間隔。我想知道這是否是一種好的做法?而且,有什麼可能是更有效的方式來實現呢?該計劃旨在隨時運行。Java while循環和線程!

因此,停止程序的唯一方法是對進程ID發出kill。該程序可能處於JDBC調用的中間。我怎麼能更優雅地終止它?我知道最好的選擇是通過使用一個將由線程定期檢查的標誌來設計某種退出策略。但是,我無法想象改變這個標誌的價值的方式/條件。有任何想法嗎?

回答

12

我想知道這是否是一種好的做法?

不,它不好。有時候,這只是你所擁有的,但並不好。

而且,有什麼可能是更有效的方法來實現呢?

事情到底如何進入數據庫?

最好的修改是修復插入/更新數據庫的程序,使請求進入數據庫和程序。 JMS主題適合這種事情。

下一個最好的更改是向數據庫添加一個觸發器,以將每個插入/更新事件排入隊列。隊列可以提供一個JMS主題(或隊列)供您的程序處理。

回退計劃是您的投票循環。

但是,您的輪詢循環不應該不起作用。它應該將消息放入一個隊列中,以供其他JDBC進程使用。終止請求是另一個可以放入JMS隊列的消息。當程序獲得終止消息時,必須完成之前的JDBC請求並可以正常停止。

在做任何這些之前,請看ESB解決方案。 Sun的JCAPSTIBCO已經有這個。像MulesourceJitterbit這樣的開源ESB可能已經具有已經構建和測試過的此功能。

+0

+1全面解答! – 2012-06-06 18:53:45

8

這對於以這種格式完全回答的問題實在太大了。幫你一個忙,去買Java Concurrency in Practice。在那裏的Java 5+平臺沒有更好的併發資源。有整章致力於這個問題。

關於在JDBC調用期間殺死進程的問題,應該沒問題。我相信有中斷JDBC調用的問題(因爲你不能這麼做),但這是一個不同的問題。

+1

+1如果您發現Goetz的書有點壓倒性的(我剛開始的時候有很多的信息),我建議您閱讀Java Threads 3rd edition,以獲得更多的代碼。 – blank 2008-12-23 22:40:54

+0

有沒有關於這個特定問題的總結?像「使用這個類代替」?或者其他的東西? – OscarRyz 2008-12-23 22:42:20

2

爲SIGTERM設置一個信號處理程序,該信號處理程序設置一個標誌,告訴您的循環在下一次通過時退出。

+0

「爲SIGTERM設置信號處理程序」您如何做到這一點? – OscarRyz 2008-12-23 22:43:21

+0

谷歌'java信號處理程序'。 – chaos 2008-12-24 06:49:13

0

如果這是你的應用程序,你可以修改它,您可以:

  • 讓它讀取文件
  • 讀爲標誌的價值。
  • 當你想殺死它,你只需修改文件,應用程序將優雅地退出。

不需要工作,那就更難了。

3

注意,一個計時器(或類似)將是你至少可以重複使用它,讓它與所有的睡眠,調度,異常處理等細節做的更好......

有您的應用程序可能死亡的很多原因不要只關注那個。

如果你的JDBC工作甚至在理論上可能使事情處於半正確狀態,那麼你有一個應該修復的錯誤。你所有的數據庫工作都應該在交易中。它應該去或不去。

1

關於「程序可能在JDBC調用中,我怎麼能更好地終止它?」 - 請參閱How can I abort a running jdbc transaction?

請注意,使用帶sleep()的輪詢很少是正確的解決方案 - 執行不當,最終可能導致佔用CPU資源(JVM線程調度程序最終花費過多的時間睡眠並喚醒線)。

3

這是Java。將您的處理移至第二個線程。現在您可以

  • 從stdin中讀取一個循環。如果有人輸入「QUIT」,請將while標誌設置爲false並退出。
  • 用STOP按鈕創建AWT或Swing框架。
  • 假裝你是一個Unix守護進程並創建一個服務器套接字。等待某人打開套接字併發送「QUIT」。 (這有額外的好處,你可以改變睡眠選擇超時。)

必須有數百個變種對此。

0

我創建了一個服務類在我目前的公司的公用庫這類問題:

public class Service implements Runnable { 

    private boolean shouldStop = false; 

    public synchronized stop() { 
     shouldStop = true; 
     notify(); 
    } 

    private synchronized shouldStop() { 
     return shouldStop; 
    } 

    public void run() { 
     setUp(); 
     while (!shouldStop()) { 
      doStuff(); 
      sleep(60 * 1000); 
     } 
    } 

    private synchronized sleep(long delay) { 
     try { 
      wait(delay); 
     } catch (InterruptedException ie1) { 
      /* ignore. */ 
     } 
    } 

} 

當然,這還遠遠沒有完成,但你應該得到的要點。這將使您能夠在程序停止時簡單地調用stop()方法,並且它將完全退出。

6

正如其他人所說,事實上,你必須進行民意調查可能表明你的系統設計有更深層次的問題......但有時候這是它的方式,所以......

如果你想處理「殺」的過程中多了幾分優雅,你可以安裝一個關閉掛鉤,當你打按Ctrl +ç其中被稱爲:

volatile boolean stop = false; 

Runtime.getRuntime().addShutdownHook(new Thread("shutdown thread") { 

    public void run() { 

     stop = true; 
    } 
}); 

然後定期檢查停止變量。

一個更優雅的解決方案是等待一個事件:

boolean stop = false; 

final Object event = new Object(); 

Runtime.getRuntime().addShutdownHook(new Thread("shutdown thread") { 

    public void run() { 

     synchronized(event) { 

      stop = true; 
      event.notifyAll(); 
     } 
    } 
}); 

// ... and in your polling loop ... 
synchronized(event) { 

    while(!stop) { 

     // ... do JDBC access ... 
     try { 

      // Wait 30 seconds, but break out as soon as the event is fired. 
      event.wait(30000); 
     } 

     catch(InterruptedException e) { 

      // Log a message and exit. Never ignore interrupted exception. 
      break; 
     } 
    } 
} 

或者類似的東西。

0

您可以使該字段成爲包含(概念上)進程ID和時間戳的複合值。 [更好的是,使用兩個或多個字段。]在擁有對字段訪問權的進程中啓動一個線程,並讓它循環,休眠和更新時間戳。然後等待自己訪問該字段的輪詢過程可以觀察到時間戳在某個時間T(它比更新循環的休眠間隔的時間大得多)沒有更新,並且假定先前擁有的過程已經死亡。

但這仍然容易失敗。

在其他語言中,我總是嘗試使用flock()調用來同步文件。不確定Java的等價物是什麼。如果你有可能的話,可以獲得真正的併發性。

0

我很驚訝沒有人提到在Java中實現的中斷機制。它應該是停止線程問題的解決方案。所有其他解決方案至少有一個缺陷,這就是爲什麼需要在Java併發庫中實現此機制的原因。

您可以通過發送一箇中斷()消息來停止線程,但也有其他線程被中斷的方式。發生這種情況時會拋出InterruptedException。這就是爲什麼在調用sleep()時必須處理它的原因。這就是您可以清理並正常結束的地方,如關閉數據庫連接。

0

我想你應該用timertask來輪詢它。

我的電腦在10秒內運行一個while循環1075566次。 這就是一秒鐘的107557次。

真的需要多久進行一次輪詢? TimerTask在1秒內以最快的速度運行1000次。你以int(毫秒)爲參數給它一個參數。如果你對此滿意 - 這意味着你在這個任務中壓縮你的cpu少了108倍。

如果您很滿意每秒輪詢一次(108 * 1000)。減少108 000次。這也意味着你可以檢查108 000個值,與你使用同一個循環的cpu應變相同 - 因爲你不會像過去那樣指定你的cpu來檢查。記住CPU有一個時鐘週期。礦井爲36億000赫茲(每秒週期)。

如果您的目標是讓用戶更新 - 您可以在用戶每次登錄時運行一次檢查(或手動讓他請求更新) - 這實際上不會給CPU帶來任何負擔。

您還可以使用thread.sleep(miliseconds);來降低輪詢線程的壓力(因爲它不會經常輪詢)您在哪裏做。

0

Java9還有一個「勢」的回答是:Thread.onSpinWait()

表示呼叫者暫時無法取得進展,直到對其他活動的部分一個或多個動作的發生。通過在spin-wait循環結構的每次迭代中調用此方法,調用線程會向運行時指示它正在等待忙碌。運行時可能會採取行動來提高調用自旋等待循環結構的性能。

查看JEP 285瞭解更多詳情。