Java 多个对单个线程的同时访问

Java 多个对单个线程的同时访问,java,multithreading,networking,Java,Multithreading,Networking,我目前正在用Java通过数据报套接字/数据包使用UDP构建多线程服务器/客户机。我很难理解线程的正确使用,希望得到一些澄清。我先举一个我正在做的例子 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

我目前正在用Java通过数据报套接字/数据包使用UDP构建多线程服务器/客户机。我很难理解线程的正确使用,希望得到一些澄清。我先举一个我正在做的例子

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

    }

}
包服务器;
导入java.io.IOException;
导入java.net.DatagramPacket;
导入java.net.DatagramSocket;
导入java.net.InetAddress;
导入java.util.Vector;
公共类ServerSenderThread扩展线程
{
DatagramSocket服务器套接字;
Vector outGoingPackets=新向量();
公共服务器SenderThread(DatagramSocket服务器套接字)
{
this.serverSocket=serverSocket;
}
公开募捐
{
while(true)
{
if(outGoingPackets.size()=0)
{
尝试
{
睡眠(50);
}
捕捉(中断异常e)
{
e、 printStackTrace();
}
}
其他的
{
尝试
{
send();
}
捕获(IOE异常)
{
//TODO自动生成的捕捉块
e、 printStackTrace();
}
}
}
}
公共void addSend(数据包)
{
外包.附加件(包);
}
public void send()引发IOException
{
DatagramPacket packet=outGoingPackets.get(0);
移除组件(0);
InetAddress=packet.getAddress();
int-port=packet.getPort();
字节[]buf=新字节[256];
String dString=“只有服务器知道的数据”;
buf=dString.getBytes();
数据包=新数据包(buf,buf.length,address,port);
System.out.println(“服务器发送数据包”);
发送(数据包);
}
}

对于与您描述的类似的多线程应用程序,最好在线程之间使用消息传递。这将是自动线程安全的,并且完全按照您在
put(message)
take()
中描述的那样,免提消息


例如,您的数据包侦听器线程可以有一个
BlockingQueue
,它
s消息放在数据包解析器
从中获取的消息上。

数据包的发送和接收通常很简单,除非您的速率很高,例如10+K/秒。这些数据包的处理可能需要一些时间,但是除非这真的很贵(比解析更多),所以我会考虑使用一个线程来处理所有这些函数。它将简化代码并使调试更容易。i、 我会让设计尽可能简单,除非你知道你需要使它更复杂

如果您比较上面的单线程版本,您会发现它更简单,这是一个明显的好处,而在这种情况下使用多线程并不是一个明显的好处

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

我认为,如果a有一个套接字(或另一个资源),并且来自每个线程的每个调用都使用该资源,则可能会出现问题。如果每次调用都使用一个新的(或不同的)套接字,我想不会有问题

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

if (case)
{
    synchronized (a) 
    {
        a.somefunction();
    {
}
或者最好在somefunction定义中添加synchronized

public void synchronized somefunction() {
    ...
}
另一种方法是改变解决方案设计,没有人直接访问a,而是将要发送的数据包添加到一个列表中,该列表将由c(消费者)监控,并将发送列表中出现的每个数据包。该列表将被同步,但同步的干扰性较小,因为它只会影响将元素添加到列表中,而不会影响元素的所有处理

更新:
我还向您推荐这本书,非常直截了当的阅读,甚至这篇来自博客的评论,这是我这本书的来源。

因此,在您的示例中,您使用
a
作为线程:
a.start
,作为可运行的:
c.start(a)
,作为对象:
a.someFunction
。这是令人困惑的,这将有助于发布真正的代码。另外,如果您不确定线程是如何工作的,您可以使用更容易使用的高级并发包:啊,我在我的sudo示例中更正了类型,我可以发布真实代码,但它并没有完全实现。我确实了解线程是如何工作的,并且经常使用线程。当java允许我使用上述实现时,我很惊讶。在我将其部署到整个应用程序之前,我想知道是否会有后果。我认为
a
是一个线程。线程没有函数。嗯,添加了一个编辑,以显示一些使用函数的线程示例代码。这是一个很好的响应,有很多很好的信息。我希望并发解析数据包的原因是为了能够利用服务器上的多核。当游戏服务器完全加载(最多20个连接的客户端/会话)时,我希望运行大约50次