我有一个应用程序启动四个线程来侦听传入的数据包。每个线程在不同的端口上打开一个套接字。通常,数据包仅在一个端口上接收,但在某些情况下,可以在两个端口上接收消息几秒钟。每个线程都处理消息并更新一堆监听器(所有这些都在做一些Swing绘画的东西)。由于消息以10 Hz的频率发送,并且Swing组件上的绘制操作需要一些时间,因此我的第一种方法是仅处理20个中的一个消息(2秒时间来完成组件上的绘制)。效果很好......
但是当我接收到两条消息时,我需要告诉我的应用程序只处理其中一条消息(只能在短时间内收到)。总之,在第二端口上接收10条消息,频率也为10Hz。手段,使用第一种方法有时我会错过所有10种方法,因为只处理了20种中的一种。
每当收到第二个端口上的消息时,我希望我的应用程序处理该消息,无论在第一个端口上收到什么,或者当时是否绘制了某些内容。
以下代码显示了我的线程的实现,其中四个是根据构造函数给出的端口同时启动的。
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。每个updated
都会在不同的paintComponent()
上调用JPanel
方法。所以他们都应该一起跑。
UPDATE2: 我不能使用第一种方法,因为这会导致消息丢失可能重要的消息(在第二个端口上接收)。我需要的是,当接收到正常的Msg时,只需处理它并进行绘制,无论有多少新的正常消息(在第一个端口上)都进来。但即使收到第二个端口上只有一个Msg,应用程序也是如此无论正常接收器线程中发生了什么,都需要处理那个。
我想我在这里遇到两个问题:
我需要让每个线程等到绘画完成,因为这是UDP我可以处理正常的数据包,并在绘制操作期间忘记所有后续的正常数据包。完成后,处理下一个正常数据包。
如果收到第二个端口上的数据包,请中断所有正常的数据包处理操作,并执行处理特殊数据包所需的操作。
使用MainIncomingClass中的BitSet解决问题(1)。每个Listener都使用某种回调函数来指示它完成了绘制并在BitSet中设置了一个特定的Bit。如果不是全部都是真的,我不会处理任何新的数据包,只是让它们去。
答案 0 :(得分:2)
他们谈论事件派遣线程here。您有使用它来更新您的GUI。幸运的是,您还可以使用它以您想要的任何顺序发布更新。 EDT会为您处理start()
。您仍需要同步t
的访问权限。
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
((IPacketListener)c).update(t);
}
});