着信パケットをリッスンするために 4 つのスレッドを開始するアプリケーションがあります。すべてのスレッドは、異なるポートでソケットを開きます。通常、パケットは一度に 1 つのポートだけで受信されますが、場合によっては、メッセージが 2 つのポートで数秒間受信されることがあります。これらのスレッドのそれぞれがメッセージを処理し、一連のリスナーを更新します (それらはすべて Swing の描画処理を行っています)。メッセージは 10 Hz の頻度で送信され、Swing コンポーネントのペイント アクションには時間がかかるため、最初のアプローチは 20 のメッセージのうち 1 つだけを処理することでした (コンポーネントのペイントを完了するのに 2 秒かかります)。うまく機能します...
しかし、一度に 2 つのメッセージを受信した場合、そのうちの 1 つ (短時間だけ受信したもの) だけを処理するようにアプリケーションに指示する必要があります。要約すると、10 メッセージが 2 番目のポートで受信され、周波数も 10 Hz です。つまり、最初のアプローチを使用すると、20 のうち 1 つしか処理されないため、10 個すべてを見逃すことがあります。
2番目のポートでメッセージが受信されるたびに、アプリケーションでそのメッセージを処理したいのですが、1番目のポートで何を受信したか、その時点で何かが描画されているかどうかは関係ありません。
次のコードは、私のスレッドの実装を示しています。これらのうちの 4 つは、コンストラクターを介して指定されたポートに応じて同時に開始されます。
private class IncomingRunner implements Runnable {
private int listenPort;
private DatagramSocket localSocket;
private DatagramPacket packet;
private int counter = 0;
public IncomingRunner(int port) {
this.listenPort = port;
}
@Override
public void run() {
try {
localSocket = new DatagramSocket(listenPort);
byte[] buffer = new byte[1024];
packet = new DatagramPacket(buffer, buffer.length);
while(isRunning)
recvIncomingMsg();
} catch (SocketException e) {
e.printStackTrace();
}
}
private void recvIncomingMsg() {
try {
localSocket.receive(packet);
port = localSocket.getLocalPort();
ReceivedMsg eventMsg;
if(port == Config.PORT_1) {
eventMsg = new ReceivedMsg(Config.PORT_1, Config.SOMETHING_1);
System.out.println(HexWriter.getHex(packet.getData()));
} else if (port == Config.PORT_2) {
eventMsg = new ReceivedMsg(Config.PORT_2, Config.SOMETHING_2);
System.out.println(HexWriter.getHex(packet.getData()));
} else if (port == Config.PORT_3) {
eventMsg = new ReceivedMsg(Config.PORT_3, Config.SOMETHING_3);
System.out.println(HexWriter.getHex(packet.getData()));
} else {
eventMsg = new ReceivedMsg(Config.PORT_4, Config.SOMETHING_4);
System.out.println(HexWriter.getHex(packet.getData()));
}
counter++;
if(counter%20 == 0) {
forward2PacketPanel(eventMsg);
counter = 0;
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void forward2PacketPanel(final ReceivedMsg t) {
for(final IPacketListener c : listeners) {
if(c instanceof IPacketListener) {
new Thread(new Runnable() {
@Override
public void run() {
((IPacketListener)c).update(t);
}
}).start();
}
}
}
}
更新:
リスナーを更新するために新しいスレッドを開始する理由は、すべてのスレッドが同時に GUI を更新する必要があるためです。Everyは異なる でメソッドをupdated
呼び出します。したがって、それらはすべて一緒に実行する必要があります。paintComponent()
JPanel
更新 2: 重要なメッセージ (2 番目のポートで受信) が失われる可能性があるため、最初のアプローチは使用できません。私が必要とするのは、通常のメッセージが受信されたときに、それを処理して描画を行うだけで、(1 番目のポートで) 新しい通常のメッセージがいくつ入っても関係ありません。しかし、2 番目のポートで 1 つのメッセージしか受信されない場合でも、アプリケーション通常の受信スレッドで何が起こっているかに関係なく、それを処理する必要があります。
ここで2つの問題に直面していると思います:
描画が完了するまで各スレッドを待機させる必要があります。これは UDP であるため、描画アクション中に通常のパケットを処理し、後続のすべての通常のパケットを忘れることができます。完了したら、次の通常のパケットを処理します。
2 番目のポートでパケットを受信した場合は、通常のパケット処理アクションをすべて中断し、特別なパケットの処理に必要な処理を行います。
問題 (1) は、MainIncomingClass の BitSet を使用して解決されます。すべてのリスナーは、ある種のコールバック関数を使用して、ペイントが完了したことを示し、BitSet に特定のビットを設定します。すべてが真ではない場合、新しいパケットは処理せず、手放します。