Home > Blockchain >  Why can we invoke a method of an object from another thread in Java?
Why can we invoke a method of an object from another thread in Java?

Time:11-13

I was reading a simple multi-threaded chatroom in Java. In the program, there's a class called Chatroom, which has method broadcast. The method was called by another serverThread thread, and it printed some messages in the original thread (the chatroom thread).

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. (if you can point me to more resources to look at, I would be incredibly grateful)

Java Code

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

output

CodePudding user response:

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. There can be only one System.out object—a PrintStream object—at any given point in time.

Normally, the Java runtime environment sets up System.out to point to some useful place such as a console window. 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.

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.

I would not say "inside" though. A thread is not a container. There's nothing "inside" a thread, though there usually are some variables (e.g., 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.

CodePudding user response:

The method was called by another serverThread thread, and it printed some messages in the original thread (the chatroom thread).

This is a somewhat misleading statement depending on what you mean by the word "in". Each of the ServerThread objects gets passed the ChatRoom object and are calling back to the ChatRoom.broadcast(...) method. 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. If ThreadA is running and calls chatRoom.broadcast("foo") then it is ThreadA which executes that method. 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. 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(...). 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. There are no unprotected fields from Chatroom being accessed. Then you have to evaluate the call back to ServerThread.sendMessage(...) which uses a PrintWriter which is has an internal lock. 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.

OOP just makes things a lot more confusing for me.

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. 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. final forces the fields to be fully initialized before the ServerThread constructor finishes. This is very important because each thread is accessing the PrintWriter of the other threads and might access fields in other threads before they are properly setup. 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. Anything initialized in the constructor should be final if possible.
  2. As a rule and a continuation to the previous point, you should never start() a thread inside it's own constructor. You run the risk of having other threads access the fields in the thread before they are fully initialized. Constructing the object and then calling start() on it outside of the constructor is the right thing to do.
  3. The Chatroom object also is doing too much in its constructor. It should not be creating the other threads in its constructor which then call back to the Chatroom which might not be initialized. 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.

Hope this helps.

  • Related