0

我目前正在通过数据报套接字/数据包使用 UDP 用 Ja​​va 构建多线程服务器/客户端。我很难理解线程的正确使用,并希望得到一些澄清。我将首先举例说明我在做什么。

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 可能需要将响应发送回客户端,因此它将调用 a 中的函数,该函数将数据包排队等待发送。

再次感谢,

再次编辑,

使用函数的示例线程

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);

    }

}
4

3 回答 3

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);
    }
}
于 2013-01-12T17:41:21.780 回答
0

对于类似于您描述的多线程应用程序,最好使用BlockingQueue线程之间的消息传递。这将是自动线程安全的,并且完全按照您使用put(message)和描述的方式传递消息take()

例如,您的数据包侦听器线程可能在数据包解析器来自的消息上具有BlockingQueue它的消息。puttake

于 2013-01-12T17:32:19.267 回答
0

我认为如果 a 有一个套接字(或另一个资源)并且来自每个线程的每个调用都使用该资源,那么您可能会遇到问题。如果每个呼叫都使用一个新的(或不同的)套接字,我想不会有问题。

处理此问题的一种方法是同步访问:

if (case)
{
    synchronized (a) 
    {
        a.somefunction();
    {
}

或者更好地在 somefunction 定义中添加同步

public void synchronized somefunction() {
    ...
}

另一种方法是更改​​解决方案设计,使用生产者-消费者模式,没有人直接访问 a,但将要发送的数据包添加到由 c(消费者)监控的列表中,并将发送将出现的每个包名单。列表将被同步,但同步的侵入性会更小,因为它只会影响将元素添加到列表中,而不会影响元素的所有处理。

更新:我还向您推荐Java Concurrency In Practice一书,非常直接的阅读,甚至是来自博客The Java Specialists的这篇评论,这是我的书源。

于 2013-01-12T17:41:47.693 回答