2012-02-21 14 views
0

私は、接続を受け付けるたびにコールバック関数を持つスレッドにタイムアウトイベントを登録するように自分のサーバを変更しています。タイムアウトには任意の数値を使用できます。コールバック関数を持つスレッドにイベントを登録する

タイムアウトスレッドは1つだけです(各接続は1つのタイムアウトスレッドに割り当てられていません)。

どうすればいいですか?私はメインでスレッドを開始することを考えていますが、タイムアウトイベントを登録する方法と引数を渡す方法はわかりません。

+0

「java.util.Timer」には必要な機能がありません。 –

答えて

3

ScheduledExecutorServiceを使用します。

ScheduledExecutorService ses = Executors.newScheduledThreadPool(1); 
void registerCallback() { 
    ses.schedule(new MyCommand(), 30, TimeUnit.SECONDS); 
} 

それはあなたがする、またはMyCommandによって返された値を取得したい場合は、実行をキャンセルするために使用することができますFutureを返します。

タイムリーに繰り返し実行するコマンドをスケジュールする場合は、scheduleAtFixedRatescheduleWithFixedDelayという他のスケジューリング方法を使用できます。

いくつかの条件、または異なるレートまたは間隔でスケジュールを変更する必要がある場合は、ScheduledExecutorServiceをコマンド(つまりnew MyCommand(ses))に渡して、それ自体または新しいコマンドを使用して再スケジュールさせます。適切な遅延:

class MyCommand implements Runnable { 
    private final ScheduledExecutorService ses; 
    MyCommand(ScheduledExecutorService ses) { this.ses = ses; } 
    private boolean shouldReschedule() { ... } 
    private int getRescheduleTimeoutMs() { ... } 
    @Override void run() { 
    // do work 
    ... 
    // reschedule if needed 
    if (shouldReschedule()) { 
     // reschedule this command: 
     ses.schedule(this, getRescheduleTimeoutMs(), TimeUnit.MILLISECONDS); 
     // or else a new one: 
     ses.schedule(new MyCommand(ses), ...); 
    } 
    } 
} 
+1

これは魅力的に機能しました! –

-2

@ Brunoの回答のような音があなたのために働いた。後世のために、別の方法は、接続が確立されているかどうかを確認し、年齢を確認するために毎秒目覚めするバックグラウンドスレッドを持つだけです。あなたはより多くのファンシーを取得したい場合は、開始時間を設定し、秒の適切な量を眠ることができます。次のような擬似コードがあります。

private AtomicReference<Socket> socketTimeoutRef = new AtomicReference<Socket>(); 
public void run() { 
    while (true) { 
     Thread.sleep(1000); 
     SocketTimeout socketTimeout = socketTimeoutRef.get(); 
     if (socketTimeout == null) { 
      continue; 
     } 
     if (socket is closed) { 
      socketTimeoutRef.compareAndSet(socketTimeout, null); 
     } else if (System.currentTimeMillis() >= socketTimeout.timeoutMillis) { 
      try { 
       close the socket; 
      } catch (Exception e) { 
       // ignore exceptions 
      } 
      socketTimeoutRef.compareAndSet(socketTimeout, null); 
     } 
    } 
} 
public void startTimeoutCounter(Socket socket) { 
    socketRef.set(new SocketTimeout(socket)); 
} 
private static class SocketTimeout { 
    Socket socket; 
    long timeoutMillis; 
    public SocketTimeout(Socket socket) { 
     this.socket = socket; 
     timeoutMillis = System.currentTimeMillis() + SOCKET_TIMEOUT_MILLIS; 
    } 
} 
+0

タイムリーなコードの繰り返しを望むなら、複雑でエラーを起こしやすいものを書く必要はありません。 'ScheduledExecutorService':' scheduleAtFixedRate'と 'scheduleWithFixedDelay'という全く同じインタフェースで、これをよりスケーラブルな方法で正確に達成する方法があります。何らかの条件や遅延の変化を繰り返す場合は、 'SES'を' command'に渡して、自分自身(または新しいコマンド)を再度スケジューリングするかどうかを決定させてください。 –

+0

私はブルノに同意しない。スケジュールされた方法のいずれかを使用していた場合は、このコードの半分を複製する必要があります。たとえば、キャンセルするレースが存在するように、他のスレッドがスレッドをきれいに終了した場合、そのスレッドが閉じることができると仮定します。私はちょうど完了しようとしていた。 executorサービスを使用して保存したのは、 'sleep(1000)'ループだけです。 – Gray

+0

ゲイリー、申し訳ありませんが、私はあなたが心配していることを理解していません(*レースキャンセル*?)。私はあなたがそれについて精緻化することができたらうれしく思います、そして、このコードが標準的なScheduledExecutorService(またはThreadPoolScheduledExecutor、直接)でそれができない(またはそれは実際には嫌な)ことができます。たとえば、SESのアプローチでは、Thread.sleep(...)(および省略したThrowされたチェック済みのInterruptedException)と面倒なヌル・チェックとセットを避けることになります。ボイラープレートコード。 –

関連する問題