简体   繁体   English

为什么我们可以调用来自 Java 中另一个线程的对象的方法?

[英]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:我的问题是:

  1. 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?我们不是必须做某种“信号”或将某些东西放入共享数据空间,以便另一个线程中的方法可以自发地采取相应的行动吗?
  2. Even if it is possible.即使有可能。 Why does it output not in the caller thread but in the thread in which it is defined?为什么它不在调用者线程中输出,而是在定义它的线程中输出?
  3. A more general questions is I guess: how are code translated and executed in case of multi-threading?我猜一个更普遍的问题是:在多线程的情况下如何翻译和执行代码? OOP just makes things a lot more confusing for me. OOP 只是让我感到更加困惑。 (if you can point me to more resources to look at, I would be incredibly grateful) (如果你能指出我更多的资源来查看,我将非常感激)

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线程中打印

output输出

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.希望下面的内容不会让您感到困惑,因为这有点像是校队的话题。

  1. All of the fields in the 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.是的,这是疯狂的东西,但这是语言定义的一部分。 For that matter, it would be a good pattern to have Chatroom.serverThreads also be final .就此而言,让Chatroom.serverThreads也是final将是一个很好的模式。 Anything initialized in the constructor should be final if possible.如果可能,在构造函数中初始化的任何内容都应该是final的。
  2. As a rule and a continuation to the previous point, you should never 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.您冒着让线程在初始化之前访问它自己的字段或让其他线程访问未初始化的字段的风险。 Constructing the object and then calling start() on it is the right thing to do.构造对象然后对其调用start()是正确的做法。
  3. The 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM