为什么我们可以在Java中从另一个线程调用一个对象的方法?
Why can we invoke a method of an object from another thread in Java?
我正在阅读 Java 中的一个简单的多线程聊天室。在程序中,有一个名为Chatroom
的class,它有方法broadcast
。该方法被另一个 serverThread
线程调用,并在原始线程(聊天室线程)中打印了一些消息。
我对此完全感到困惑。我的问题是:
- 怎么可能像那样从另一个线程调用一个方法呢?难道我们不需要做某种“信号”或将某些东西放入共享数据 space 以便另一个线程中的方法可以自发地采取相应行动吗?
- 即使有可能。为什么它不在调用者线程中输出,而是在定义它的线程中输出?
- 我想还有一个更普遍的问题:在多线程的情况下如何翻译和执行代码? OOP 只会让我更加困惑。 (如果您能指出更多资源供我查看,我将不胜感激)
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
}
这是输出。在我看来,“广播消息”不是在 ServerThread
线程中打印的(顺便说一句,我不知道如何显示其输出),而是在 Chatroom
线程中
output
is there only one stdout that all threads print to?
System
是 class 的名称,System.out
是 class 的 static
成员的名称。在任何给定时间点只能有一个 System.out
对象——一个 PrintStream
对象。
通常,Java 运行时环境设置 System.out
以指向一些有用的地方,例如控制台 window。但是 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?
是的。这就是线程所做的。他们执行你的代码。每个线程开始在某个 Runnable
实例的 run()
方法中执行您的代码,并且它会继续执行您的代码从该点开始执行的任何操作,直到 (a) 它到达run()
方法,或 (b) 它抛出未捕获的异常。
虽然我不会说“里面”。线程不是容器。线程“内部”没有任何内容,尽管通常有一些变量(例如,线程调用的所有函数的所有局部变量)其他线程不会或不能访问。
* 图书馆 可能 这样做,但除非文档 非常 清楚会发生什么。
The method was called by another serverThread thread, and it printed some messages in the original thread (the chatroom thread).
根据您所说的“在”一词的含义,这是一个有点误导性的陈述。每个 ServerThread
对象都传递给 ChatRoom
对象并回调到 ChatRoom.broadcast(...)
方法。不要因为它们是线程而感到困惑。这只是一个对象调用另一个对象的方法。
Why does it output not in the caller thread but in the thread in which it is defined?
因为是 caller 线程在进行调用。仅仅因为您在线程方法之间来回调用并不意味着不同的线程正在进行调用。该练习试图混淆。同样,这些只是调用其他对象的对象,这里没有神奇的线程信号。
how are code translated and executed in case of multi-threading?
代码在 Java OOP 中的工作方式相同,无论您是否有多个线程。如果 ThreadA 是 运行ning 并调用 chatRoom.broadcast("foo")
那么它是 ThreadA 执行该方法。如果该方法转过来并对每个 serverThread.sendMessage(foo)
进行一系列调用,那么它仍然是 ThreadA,它正在 运行 并进行这些方法调用。您实际上必须做很多工作才能在线程之间移交控制。
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?
仅仅因为一个线程调用另一个线程的方法并不以任何方式暗示存在任何信号或自动数据共享。这在很大程度上取决于正在访问的数据。
让我们看看通话的每一部分。 ThreadA 回调 chatroom.broadCast(...)
。在该方法中 message
可以被访问,因为它是传入的, System.out.println(...)
是一个 synchronized
PrintStream
也可以,所以它可以被使用,并且 ArrayBlockingQueue
也是 synchronized
这样就可以了。 Chatroom
中没有未受保护的字段被访问。然后,您必须评估对 ServerThread.sendMessage(...)
的回调,它使用具有内部锁的 PrintWriter
。所以这是犹太洁食。同样,如果对每个 ServerThread
对象的本地字段有一些访问权限,则需要以某种方式锁定它。
OOP just makes things a lot more confusing for me.
我认为这是 问题,在这种情况下我认为这是一个学术练习。就如何弄清楚它在做什么而言,我会打印出 Thread.currentThread().getId()
这样您就可以看到 哪个 线程正在打印什么。我还会减少线程数,然后再按比例增加,直到您可以跟上为止。
逐行查看并记住线程不会将控制权移交给其他线程,除非您会看到特定的调用。
警告:练习中有几个错误需要修复。希望以下内容不会让您感到困惑,因为这有点像校队话题。
ServerThread
中的所有字段都应标记为 final
。 final
forces the fields to be fully initialized 在 ServerThread
构造函数完成之前。这非常重要,因为每个线程都在访问其他线程的 PrintWriter
,并且可能在正确设置之前访问其他线程中的字段。是的,这是疯狂的东西,但它是语言定义的一部分。就此而言,Chatroom.serverThreads
也是 final
是一个很好的模式。如果可能,在构造函数中初始化的任何内容都应该是 final
。
- 作为上一点的规则和延续,您应该 never
start()
a thread inside it's own constructor。您 运行 让其他线程在完全初始化之前访问线程中的字段的风险。构造对象然后在构造函数之外对其调用 start()
是正确的做法。
Chatroom
对象在其构造函数中也做了太多事情。它不应该在其构造函数中创建其他线程,然后回调到可能未初始化的 Chatroom
。构造套接字和 serverThreads
应该在构造函数中,但是创建新服务器线程的 while
循环应该在构造函数完成后在另一个方法中完成。
希望这对您有所帮助。
我正在阅读 Java 中的一个简单的多线程聊天室。在程序中,有一个名为Chatroom
的class,它有方法broadcast
。该方法被另一个 serverThread
线程调用,并在原始线程(聊天室线程)中打印了一些消息。
我对此完全感到困惑。我的问题是:
- 怎么可能像那样从另一个线程调用一个方法呢?难道我们不需要做某种“信号”或将某些东西放入共享数据 space 以便另一个线程中的方法可以自发地采取相应行动吗?
- 即使有可能。为什么它不在调用者线程中输出,而是在定义它的线程中输出?
- 我想还有一个更普遍的问题:在多线程的情况下如何翻译和执行代码? OOP 只会让我更加困惑。 (如果您能指出更多资源供我查看,我将不胜感激)
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
}
这是输出。在我看来,“广播消息”不是在 ServerThread
线程中打印的(顺便说一句,我不知道如何显示其输出),而是在 Chatroom
线程中
output
is there only one stdout that all threads print to?
System
是 class 的名称,System.out
是 class 的 static
成员的名称。在任何给定时间点只能有一个 System.out
对象——一个 PrintStream
对象。
通常,Java 运行时环境设置 System.out
以指向一些有用的地方,例如控制台 window。但是 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?
是的。这就是线程所做的。他们执行你的代码。每个线程开始在某个 Runnable
实例的 run()
方法中执行您的代码,并且它会继续执行您的代码从该点开始执行的任何操作,直到 (a) 它到达run()
方法,或 (b) 它抛出未捕获的异常。
虽然我不会说“里面”。线程不是容器。线程“内部”没有任何内容,尽管通常有一些变量(例如,线程调用的所有函数的所有局部变量)其他线程不会或不能访问。
* 图书馆 可能 这样做,但除非文档 非常 清楚会发生什么。
The method was called by another serverThread thread, and it printed some messages in the original thread (the chatroom thread).
根据您所说的“在”一词的含义,这是一个有点误导性的陈述。每个 ServerThread
对象都传递给 ChatRoom
对象并回调到 ChatRoom.broadcast(...)
方法。不要因为它们是线程而感到困惑。这只是一个对象调用另一个对象的方法。
Why does it output not in the caller thread but in the thread in which it is defined?
因为是 caller 线程在进行调用。仅仅因为您在线程方法之间来回调用并不意味着不同的线程正在进行调用。该练习试图混淆。同样,这些只是调用其他对象的对象,这里没有神奇的线程信号。
how are code translated and executed in case of multi-threading?
代码在 Java OOP 中的工作方式相同,无论您是否有多个线程。如果 ThreadA 是 运行ning 并调用 chatRoom.broadcast("foo")
那么它是 ThreadA 执行该方法。如果该方法转过来并对每个 serverThread.sendMessage(foo)
进行一系列调用,那么它仍然是 ThreadA,它正在 运行 并进行这些方法调用。您实际上必须做很多工作才能在线程之间移交控制。
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?
仅仅因为一个线程调用另一个线程的方法并不以任何方式暗示存在任何信号或自动数据共享。这在很大程度上取决于正在访问的数据。
让我们看看通话的每一部分。 ThreadA 回调 chatroom.broadCast(...)
。在该方法中 message
可以被访问,因为它是传入的, System.out.println(...)
是一个 synchronized
PrintStream
也可以,所以它可以被使用,并且 ArrayBlockingQueue
也是 synchronized
这样就可以了。 Chatroom
中没有未受保护的字段被访问。然后,您必须评估对 ServerThread.sendMessage(...)
的回调,它使用具有内部锁的 PrintWriter
。所以这是犹太洁食。同样,如果对每个 ServerThread
对象的本地字段有一些访问权限,则需要以某种方式锁定它。
OOP just makes things a lot more confusing for me.
我认为这是 问题,在这种情况下我认为这是一个学术练习。就如何弄清楚它在做什么而言,我会打印出 Thread.currentThread().getId()
这样您就可以看到 哪个 线程正在打印什么。我还会减少线程数,然后再按比例增加,直到您可以跟上为止。
逐行查看并记住线程不会将控制权移交给其他线程,除非您会看到特定的调用。
警告:练习中有几个错误需要修复。希望以下内容不会让您感到困惑,因为这有点像校队话题。
ServerThread
中的所有字段都应标记为final
。final
forces the fields to be fully initialized 在ServerThread
构造函数完成之前。这非常重要,因为每个线程都在访问其他线程的PrintWriter
,并且可能在正确设置之前访问其他线程中的字段。是的,这是疯狂的东西,但它是语言定义的一部分。就此而言,Chatroom.serverThreads
也是final
是一个很好的模式。如果可能,在构造函数中初始化的任何内容都应该是final
。- 作为上一点的规则和延续,您应该 never
start()
a thread inside it's own constructor。您 运行 让其他线程在完全初始化之前访问线程中的字段的风险。构造对象然后在构造函数之外对其调用start()
是正确的做法。 Chatroom
对象在其构造函数中也做了太多事情。它不应该在其构造函数中创建其他线程,然后回调到可能未初始化的Chatroom
。构造套接字和serverThreads
应该在构造函数中,但是创建新服务器线程的while
循环应该在构造函数完成后在另一个方法中完成。
希望这对您有所帮助。