我正在用Java编写一个多线程程序,我正在创建一个单独的线程来处理每个新的客户端连接。我有:
Socket s;
while(true)
{
s = server.accept();
ClientHandler ch = new ClientHandler(s);
Thread servingThread = new Thread(ch);
servingThread.start();
}
在ClientHandler线程中我有:
public class ClientHandler implements Runnable
{
private Socket s;
public ClientHandler(Socket _Socket, boolean _accepted, BaseStation _bs)
{
this.s = _Socket;
}
如果在Java中我不能传递一个对象而只是一个对它的引用,那不会导致问题,只要s
传递给ClientHandler中的server
实例接受新连接?
它是否也不会在ClientHandler
内部发生变化并破坏它?如果是这样,那么正确的方法是什么?
答案 0 :(得分:2)
请参阅Is Java "pass-by-reference" or "pass-by-value"?进行解释。
将新的Socket
实例传递给新的ClientHandler
实例很好,因为每个ClientHandler
都有自己的Socket
- 更改了局部变量{{1}的值不影响s
内Socket
实例的副本。
this 之类的东西会导致问题:
ClientHandler
因为ClientHandler ch = new ClientHandler(s);
Thread servingThread = new Thread(ch);
servingThread.start();
s.someMethod(); // close, read etc.
和ch.s
都指向相同的基础s
对象实例。
答案 1 :(得分:2)
您的顾虑是正确的,因为Socket
对象(与Java中的任何其他对象一样)是通过引用传递的,因此它有可能被不同的模块或不同的线程引用,从而导致不可预测的行为。 / p>
但是,如果你的程序没有问题。原因是:每当服务器接受新连接时,它将创建一个 new Socket
对象,然后将其传递给{{1}的 new 实例。 }。因此,来自客户端的每个新连接都将由独立的ClientHandler
实例提供服务,因此ClientHandler
对象无需担心竞争条件。所以你现在安全了。
作为一个反例,如果您决定创建两个Socket
个线程来读取相同的 ClientHandler
对象,就像这样......
Socket
...然后您可能遇到麻烦,因为两个s = server.accept();
ClientHandler ch = new ClientHandler(s);
ClientHandler ch2 = new ClientHandler(s);
new Thread(ch).start();
new Thread(ch2).start();
将获得与相同的 ClientHandler
对象的相同的引用,如果两者都试图从同一个套接字读取它们每个只获得一半的数据。例如。如果您收到字符串Socket
,则最终可能会"hello"
阅读ch
和"hel"
阅读ch2
或任何其他组合。
答案 2 :(得分:1)
这有点令人困惑,但您的代码应该如图所示。 Java首先按值传递,然后按引用传递。 I.E.,您没有传递指向“Socket s
”的指针,而是传递“s
”时的实际值。
为了简化并消除混淆,我只需删除您的“Socket s;
”前向声明,并在循环“Socket s = server.accept();
”中设置第一行。