我目前正在使用UDP通过数据报套接字/数据包构建Java中的多线程服务器/客户端。我很难理解线程的正确使用,并希望得到一些澄清。我首先举例说明我正在做的事情。
Thread a;
Thread b(a);
a.start
b.start
//simple enough, now inside b imagine this,
Thread c(a);
if (case)
{
c.start //therefore I can have a lot of thread c's running at once,
}
//now inside c imagine this
if (case)
{
a.somefunction();
}
最终我的问题很难问,但上面的sudo是否恰当地使用了线程?即使一次只运行一个线程,也可以同时从多个其他位置访问它。这会引起问题吗?
感谢您的回复。
- 威廉
只需添加编辑以进一步说明。
线程a将是我的数据包发送方,它将数据包从服务器发送到客户端。 线程b将是我的数据包侦听器,它从客户端接收数据包,并将它们发送到线程C(数据包解析器)。 (所以我可以同时解析多个数据包)。 线程c(数据包解析器)可能需要将响应发送回客户端,因此它将调用一个函数,该函数将数据包排队等待发送。
再次感谢,
再次编辑,
使用函数的示例线程
package server;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Vector;
public class ServerSenderThread extends Thread
{
DatagramSocket serverSocket;
Vector<DatagramPacket> outGoingPackets = new Vector<DatagramPacket>();
public ServerSenderThread(DatagramSocket serverSocket)
{
this.serverSocket = serverSocket;
}
public void run()
{
while (true)
{
if (outGoingPackets.size() == 0)
{
try
{
Thread.sleep(50);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
else
{
try
{
send();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public void addSend(DatagramPacket packet)
{
outGoingPackets.addElement(packet);
}
public void send() throws IOException
{
DatagramPacket packet = outGoingPackets.get(0);
outGoingPackets.removeElementAt(0);
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] buf = new byte[256];
String dString = "Data Only the Server Knows";
buf = dString.getBytes();
packet = new DatagramPacket(buf, buf.length, address, port);
System.out.println("Sserver sending packet");
serverSocket.send(packet);
}
}
答案 0 :(得分:1)
发送和接收数据包通常很简单,除非您的费率很高,例如10 + K /秒。处理这些数据包可能需要一些时间,但除非这非常昂贵(远远超过解析),我会考虑使用一个线程来完成所有这些功能。它将简化代码并使调试更容易。即使你知道你需要让它变得更复杂,我会尽可能简化设计。
如果你比较上面所用的单线程版本,你可以看到它更简单,这是一个明显的好处,而在这种情况下使用多线程并不是一个明显的好处。
public class DataPacket {
final DatagramSocket serverSocket;
public DataPacket(InetAddress address, int port) throws SocketException {
this.serverSocket = new DatagramSocket(port, address);
}
public void send(String message) throws IOException {
byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
serverSocket.send(new DatagramPacket(bytes, bytes.length));
System.out.println("Sent " + message);
}
}
答案 1 :(得分:0)
对于与您描述的类似的多线程应用程序,最好使用BlockingQueue
在线程之间传递消息。这将自动进行线程安全,并完全按照您使用put(message)
和take()
描述的方式处理消息。
例如,您的数据包侦听器线程可能有BlockingQueue
个put
来自数据包解析器take
的消息。
答案 2 :(得分:0)
我认为如果有一个套接字(或其他资源)并且来自每个线程的每个调用都使用该资源,则可能会出现问题。如果每个呼叫使用一个新的(或不同的)套接字,我猜不会有问题。
处理此问题的一种方法是同步访问:
if (case)
{
synchronized (a)
{
a.somefunction();
{
}
或更好地在somefunction定义中添加synchronized
public void synchronized somefunction() {
...
}
另一种方法是使用producer-consumer pattern更改解决方案设计,没有人直接访问a,但添加数据包以发送到将由c(消费者)监控的列表,并将发送每个包将出现在列表中。该列表将被同步,但同步将不那么具有干扰性,因为它只会影响将元素添加到列表中而不会影响元素的所有处理。
更新: 我还向您推荐了这本书Java Concurrency In Practice,非常直接的阅读,甚至还有来自博客The Java Specialists的评论,这是本书的来源。