[英]WHY can we invoke a method of an object that is FROM another thread in Java?
I was reading a simple multi-threaded chatroom in Java.我正在阅读一个简单的 Java 多线程聊天室。 In the program, there's a class called
Chatroom
, which has method broadcast
.在程序中,有一个叫做
Chatroom
的类,它有方法broadcast
。 The method was called by another serverThread
thread, and it printed some messages in the original thread (the chatroom thread).该方法由另一个
serverThread
线程调用,并在原始线程(聊天室线程)中打印一些消息。
I am totally confused by this.我对此完全困惑。 My questions are:
我的问题是:
Java Code Java代码
public class ChatRoom {
private ArrayBlockingQueue<ServerThread> serverThreads; // List<ChatRoom.ServerThread>
// Entrance of the place
public static void main(String [] args)
{
new ChatRoom(6789);
}
public ChatRoom(int port)
{
try
{
System.out.println("Binding to port " + port);
ServerSocket ss = new ServerSocket(port);
serverThreads = new ArrayBlockingQueue<ServerThread>(5); // new ArrayList<>();
while(true)
{
Socket s = ss.accept(); // Accept the incoming request
System.out.println("Connection from " + s + " at " + new Date());
ServerThread st = new ServerThread(s, this); //connection handler
System.out.println("Adding this client to active client list");
serverThreads.add(st);
}
}
catch (Exception ex) {
System.out.println("Server shut down unexpectedly.");
return;
}
}
public void broadcast(String message)
{
if (message != null) {
System.out.println("broadcasting ..." + message);
for(ServerThread threads : serverThreads)
threads.sendMessage(message);
}
}
}
public class ServerThread extends Thread {
private PrintWriter pw;
private BufferedReader br;
private ChatRoom cr;
private Socket s;
public ServerThread(Socket s, ChatRoom cr)
{
this.s = s;
this.cr = cr;
try
{
pw = new PrintWriter(s.getOutputStream(), true);
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
start();
}
catch (IOException ex) {ex.printStackTrace();}
}
public void sendMessage(String message)
{
pw.println(message);
}
public void run()
{
try {
while(true)
{
String line = br.readLine();
//if(line == null) break; // client quits
cr.broadcast(line); // Send text back to the clients
}
}
catch (Exception ex) {ex.printStackTrace();}
finally {
try {
pw.close();
br.close();
s.close();
}
catch (Exception ex) {ex.printStackTrace();}
}//finally
}//run
}
And here is the output.这是输出。 It seems to me that the "broadcasting messages" are printed not in
ServerThread
thread (which btw I don't know how to show the output of), but in Chatroom
Thread在我看来,“广播消息”不是在
ServerThread
线程中打印的(顺便说一句,我不知道如何显示其输出),而是在Chatroom
线程中打印
is there only one stdout that all threads print to?
所有线程都打印到只有一个标准输出吗?
System
is the name of a class, and System.out
is the name of a static
member of that class. System
是类的名称, System.out
是该类的static
成员的名称。 There can be only one System.out
object—a PrintStream
object—at any given point in time.在任何给定的时间点只能有一个
System.out
对象 — PrintStream
对象。
Normally, the Java runtime environment sets up System.out
to point to some useful place such as a console window.通常,Java 运行时环境设置
System.out
以指向一些有用的位置,例如控制台窗口。 But System.out
is not final
, so your program (or some library called by your program*) potentially could reassign it to send output somewhere else.但是
System.out
不是final
,因此您的程序(或您的程序*调用的某个库)可能会重新分配它以将输出发送到其他地方。
when calling an object's method in a thread, does all of its code get executed as if they are "inside" that thread?
在线程中调用对象的方法时,是否所有代码都像在该线程“内部”一样执行?
Yes.是的。 That's what threads do.
这就是线程所做的。 They execute your code.
他们执行你的代码。 Each thread starts executing your code in the
run()
method of some Runnable
instance, and it continues to do whatever your code tells it to do from that point on until either (a) it reaches the end of the run()
method, or (b) it throws an exception that is not caught.每个线程开始在某个
Runnable
实例的run()
方法中执行您的代码,并且Runnable
继续执行您的代码告诉它执行的任何操作,直到 (a) 它到达run()
方法的末尾,或者(b) 它抛出一个未被捕获的异常。
I would not say "inside" though.不过,我不会说“内部”。 A thread is not a container.
线程不是容器。 There's nothing "inside" a thread, though there usually are some variables (eg, all of the local variables of all of the functions that the thread calls) that other threads either do not or cannot access.
线程“内部”没有任何东西,尽管通常有一些其他线程不访问或无法访问的变量(例如,线程调用的所有函数的所有局部变量)。
* It is possible for a library to do that, but it would be a really rude thing for the library to do unless the documentation was very clear about what would happen. * 图书馆有可能这样做,但图书馆这样做将是一件非常粗鲁的事情,除非文档非常清楚会发生什么。
The method was called by another serverThread thread, and it printed some messages in the original thread (the chatroom thread).
该方法由另一个 serverThread 线程调用,并在原始线程(聊天室线程)中打印一些消息。
This is a somewhat misleading statement depending on what you mean by the word "in".这是一个有点误导性的陈述,具体取决于您对“in”一词的含义。 Each of the
ServerThread
objects gets passed the ChatRoom
object and are calling back to the ChatRoom.broadcast(...)
method.每个
ServerThread
对象都通过ChatRoom
对象并回调ChatRoom.broadcast(...)
方法。 Don't be confused by them being threads.不要因为它们是线程而感到困惑。 This is just one object calling a method on the other.
这只是一个对象调用另一个对象的方法。
Why does it output not in the caller thread but in the thread in which it is defined?
为什么它不在调用者线程中输出,而是在定义它的线程中输出?
Because it is the caller thread which is making the call.因为它是调用者线程进行调用。 Just because you are calling back and forth between the thread methods doesn't mean that different threads are making the calls.
仅仅因为您在线程方法之间来回调用并不意味着不同的线程在进行调用。 The exercise is trying to be confusing.
该练习试图令人困惑。 Again, these are just objects calling other objects and there is no magic thread signaling going on here.
同样,这些只是调用其他对象的对象,这里没有魔术线程信号。
how are code translated and executed in case of multi-threading?
在多线程的情况下如何翻译和执行代码?
Code works the same way in Java OOP regardless if you have multiple threads or not.无论您是否有多个线程,代码在 Java OOP 中的工作方式都是相同的。 If ThreadA is running and calls
chatRoom.broadcast("foo")
then it is ThreadA which executes that method.如果 ThreadA 正在运行并调用
chatRoom.broadcast("foo")
那么它是 ThreadA 执行该方法。 If that method turns around and makes a bunch of calls to each of the serverThread.sendMessage(foo)
then it is still ThreadA which is running and making those method calls.如果该方法转向并对每个
serverThread.sendMessage(foo)
进行大量调用,那么它仍然是 ThreadA 正在运行并进行这些方法调用。 You actually have to do a lot of work to hand off control between threads.您实际上必须做很多工作才能在线程之间传递控制权。
How is it even possible to just call a method from another thread just like that?
怎么可能就这样从另一个线程调用一个方法? Don't we have to do some kind of "signal" or putting something into a shared data space so that methods in another thread can spontaneously act accordingly?
我们不是必须做某种“信号”或将某些东西放入共享数据空间,以便另一个线程中的方法可以自发地采取相应的行动吗?
Just because the one thread calls a method on the other does not in any way imply that there any signaling or automatic data sharing.仅仅因为一个线程调用另一个线程的方法并不意味着存在任何信令或自动数据共享。 It depends highly on what data is being accessed.
这在很大程度上取决于正在访问的数据。
Let's look at every part of the calls.让我们看看调用的每个部分。 ThreadA calls back to
chatroom.broadCast(...)
. ThreadA 回调
chatroom.broadCast(...)
。 In that method message
is ok to be accessed because it is passed in, System.out.println(...)
is a synchronized
PrintStream
which is also ok so it can be used, and the ArrayBlockingQueue
is also synchronized
so that works.在该方法中,
message
可以被访问,因为它被传入, System.out.println(...)
是一个synchronized
PrintStream
,它也可以使用,并且ArrayBlockingQueue
也是synchronized
因此可以工作。 There are no unprotected fields from Chatroom
being accessed. Chatroom
中没有未受保护的字段被访问。 Then you have to evaluate the call back to ServerThread.sendMessage(...)
which uses a PrintWriter
which is has an internal lock.然后,您必须评估对
ServerThread.sendMessage(...)
的调用,它使用具有内部锁的PrintWriter
。 So this is kosher.所以这是犹太洁食。 Again, if there was some access of a field that was local to each of the
ServerThread
objects, it would need to be locked somehow.同样,如果对每个
ServerThread
对象的本地字段有一些访问,则需要以某种方式锁定它。
OOP just makes things a lot more confusing for me.
OOP 只是让我感到更加困惑。
I think it is the question which is designed to be confusing in this case I assume it is an academic exercise.我认为这是一个旨在在这种情况下令人困惑的问题,我认为这是一个学术练习。 In terms of how you can figure out what it is doing, I would print out
Thread.currentThread().getId()
so you can see which thread is printing what.就如何弄清楚它在做什么而言,我会打印出
Thread.currentThread().getId()
以便您可以看到哪个线程正在打印什么。 I would also reduce the number of threads and then scale it back up until you can follow it.我还会减少线程数,然后将其放大,直到您可以遵循它。
Going line by line and remembering that threads don't hand off control to other threads without specific calls that you would see.逐行进行并记住线程不会将控制权移交给其他线程,而无需您会看到的特定调用。
WARNING: there are a couple of bugs in the exercise that should be fixed.警告:练习中有几个错误需要修复。 Hopefully the following doesn't confuse you because this is somewhat varsity thread stuff.
希望下面的内容不会让您感到困惑,因为这有点像是校队的话题。
ServerThread
should be marked as final
. ServerThread
中的所有字段都应标记为final
。 final
forces the fields to be fully initialized before the ServerThread
constructor finishes. final
强制在ServerThread
构造函数完成之前 完全初始化字段。 This is very important because otherwise the new ServerThread
might access it's own fields before they are properly setup.ServerThread
可能会在正确设置之前访问它自己的字段。 Yeah this is crazy stuff but part of the language definition.Chatroom.serverThreads
also be final
.Chatroom.serverThreads
也是final
将是一个很好的模式。 Anything initialized in the constructor should be final
if possible.final
的。start()
a thread inside it's own constructor .start()
一个线程。 You run the risk of having the thread access it's own fields before they are initialized or to have other threads access uninitialized fields.start()
on it is the right thing to do.start()
是正确的做法。Chatroom
object also is doing too much in its constructor. Chatroom
对象在其构造函数中也做得太多了。 It should not be creating the other threads in its constructor which then call back to the Chatroom
which might not be initialized.Chatroom
。 Constructing the socket and the serverThreads
should be in the constructor but the while
loop which creates the new server threads should be done in another method after the constructor finishes.serverThreads
应该在构造函数中,但是在构造函数完成后,创建新服务器线程的while
循环应该在另一个方法中完成。 Hope this helps.希望这可以帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.