0

I have the following Thread subclass (simplified slightly for readability):

public class ConnectionHandlerThread extends Thread {
    private Socket socket;
    private volatile boolean disconnected = false;
    private ObjectOutputStream out = null;
    private ObjectInputStream in = null;

    public ConnectionHandlerThread(Socket socket){
        this.socket = socket;
    }

    public void run(){
        disconnected = false;

        try {           
            out = new ObjectOutputStream(socket.getOutputStream());
            in = new ObjectInputStream(socket.getInputStream());

            while(!disconnected){
                try{
                    Object data = in.readObject();                  
                }
                catch(ClassNotFoundException e){
                    // Handle exception
                }               
                catch(IOException e){
                    // Handle exception
                }
            }
        } 
        catch (IOException e) {
            // Handle exception
        }

    }

    public void send(Object data){
        try{
            out.writeObject( data );
        }
        catch(IOException e){
            // Handle exception
        }       
    }
}

An instance of this thread is created by my client (using Swing GUI) when I connect to the server. What I find weird about it is that I can call the method send(Object data) from the main GUI, and it works. Why doesn't the infinite while loop and/or the call to in.readObject() prevent me from doing so? I mean shouldn't the thread be busy constantly doing other stuff? Is this a good way to do things? If not, why not?

EDIT

To clarify what puzzles me: If this was in the main thread, it would be busy on that in.readObject() until something was read, and then it would just start listening again on the next iteration of the loop. Of course, I get that I can call send() from another thread. But where my mind is blown is - "who" is actually executing send()? Is my Thread doing it, or is the calling thread doing it? If it's the former, how can it both be busy waiting for input in the while loop and executing the send() method? I just have a hard time wrapping my mind around it...

Magnus
  • 17,157
  • 19
  • 104
  • 189
  • because you are passing the reference of a socket that you created in the main GUI Swing... – ΦXocę 웃 Пepeúpa ツ Mar 30 '16 at 15:05
  • Please elaborate a bit on that... – Magnus Mar 30 '16 at 15:08
  • It almost sounds as if you are confused about what a thread is. Why would you expect that the activity of one thread (e.g., reading from `in`) would interfere with the activity of some other thread (e.g., writing to `out`) when you have not established any synchronization between the two threads? Threads function independently of one another except when they operate on the same synchronization objects. – Solomon Slow Mar 30 '16 at 15:19
  • check the example in my answer – kalin Mar 30 '16 at 15:24
  • @jameslarge Yes, I am confused right now. I thought I had an understanding of what a thread was, but now I don't anymore :) I think what's confusing me is the fact that the `send()` method is defined in `ConnectionHandlerThread`. To put it another way - if I was doing `in.read()` on the main thread the UI would freeze. So what's confusing me is why `ConnectionHandlerThread` isn't "freezing", i.e. why it can execute `send()` while at the same time being blocked by `in.read()` in `run()`. I guess it's a stupid question, but until someone explains it in a good way I guess I'll just stay confused. – Magnus Mar 30 '16 at 16:13

3 Answers3

1

there 2 things:

1) an infinite loop doesnt keep the cpu busy only with itself. it just keeps it busy when constantly when its is available, but other threads my use it too.

2) When you call your

send(Object data)

you don't do it from your thread so having in mind 1) there is nothing strange it gets called

example:

Code:

public class Main {
    public static void main(String[] args) {
        InfiniteThread t = new InfiniteThread();
        t.start();
        for(int i=0;i<10;i++) {
            t.fromOut(i);
        }
    }
    @DebugLog
    public static class InfiniteThread extends Thread {

        public void run() {
            for(int i=0;i<10;i++) {
                fromIn();
            }
        }

        private void fromIn() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void fromOut(Object data){
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Output:

InfiniteThread :: ⇢ () [Thread:"main"] InfiniteThread :: ⇠ [0ms] InfiniteThread :: ⇢ run() [Thread:"Thread-0"] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇢ fromOut(data=0) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=1) [Thread:"main"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=2) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=3) [Thread:"main"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=4) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=5) [Thread:"main"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=6) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=7) [Thread:"main"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=8) [Thread:"main"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇢ fromOut(data=9) [Thread:"main"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromOut [500ms] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇢ fromIn() [Thread:"Thread-0"] InfiniteThread :: ⇠ fromIn [1000ms] InfiniteThread :: ⇠ run [10003ms]

kalin
  • 3,546
  • 2
  • 25
  • 31
  • After removing `@DebugLog` (what is that?) and instead using `System.out.println( Thread.currentThread().getName() );`I got this to work. And yeah, it does make things clearer. My main confusion was because the method being called is defined in the Thread, I thought the Thread was executing it. But I guess the key is that it's irrelevant **where** the method is defined - it is still the caller who executes it. Right? – Magnus Mar 30 '16 at 16:30
  • correct. @DebugLog is a log library which genered the logs https://github.com/djodjoni/hugo – kalin Mar 30 '16 at 16:35
1

@jameslarge Yes, I am confused right now...

A Thread object is not a thread: It's a thing with methods that can be used to configure, create, and interact with a thread. You are using your ConnectionHandlerThread to do all that, and you are also using it (by overriding the run() method) to define the work that the thread does, and you are also using it (by the several private fields) to represent the state of the connection that the thread operates on. That's a lot of overloading.

One author said something like, "It is easy to write a program that has too few objects. It's hard to write one that has too many objects."

I would re-write your code to use three different objects to do the three different jobs that your ConnectionHandlerThread does now:

public class Connection {
    private Socket socket;
    private volatile boolean connected = true;
    private ObjectOutputStream out = null;
    private ObjectInputStream in = null;

    public Connection(Socket socket){
        this.socket = socket;
        out = new ObjectOutputStream(socket.getOutputStream());
        in = new ObjectInputStream(socket.getInputStream());
    }

    public void send(Object data){
        try{
            out.writeObject( data );
        }
        catch(IOException e){
            handleExceptionInSend(e);
        }       
    }

    public Object receive() {
        try{
            Object data = in.readObject();                  
        }
        catch(ClassNotFoundException e){
            handleExceptionInReceive(e);
            return NULL;
        }               
        catch(IOException e){
            handleExceptionInReceive(e);
            return NULL;
        }
        return Object;
    }

    public boolean isConnected() {
        return connected;
    }

    ...
}


public class ConnectionListener implements Runnable {
    private final connection;

    public ConnectionRunner(Connection connection) {
        this->connection = connection;
    }

    public void run(){
        while(connection.isConnected()){
            Object o = connection.receive();
            if (o) {
                doSomethingWith(o);
            }
        }        
    }
}

public class Whatever {
    ...
    public void whateverelse( ) {
        Socket socket = ...;
        Connection connection = new Connection(socket);
        ConnectionListener listener = new ConnectionListener(connection);
        Thread connectionThread = new Thread(listener);

        connectionThread.start();
        ...
    }
    ...
}

The connection object knows how to send and receive data.

The listener object defines what the thread does: I.e., it receives objects from the connection until ! isConnected() and it does something with them.

And finally, the connectionThread object is what starts the thread (and can be used to wait for it to finish, etc.) if needed.

It's more code than what you wrote, but IMO, it's easier to understand because it's easier to see the responsibilities of each separate piece. Sometimes, especially if you are collaborating with other developers, making the code easy to understand is more important than making it small.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
  • Thanks! I sort of figured out the flaw in my reasoning. For some reason I had the idea that the `Thread`'s thread would be executing `send()` even when it was called from the outside... But now I realize that the location of the method definition is totally irrelevant, and that just because it is actually defined inside the "`Thread`" subclass doesn't mean the actual thread that executes the `run()` method will execute `send()` when it's called from the outside by another thread. Short answer - the `Thread` class is **not** the thread, `run()` is the thread... is that correct? – Magnus Mar 30 '16 at 18:17
  • @BadCash, pretty much. yup. – Solomon Slow Mar 30 '16 at 19:08
-1

this is working because in the SWING/GUI you are doing something like:

ConnectionHandlerThread myThread = ConnectionHandlerThread(new Socket());

now send() method is public, so you can call it from the GUI Class when you do

myThread.send(data);

the trick is that you are using a socket in the Thread to, this is all because you passed a socket in the constuctor of the class ConnectionHandlerThread

ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
  • I don't understand what the `Socket` has to do with anything? I just tried the code that @djodjo posted and it works just the same, and there is nothing passed to the constructor of the thread there... – Magnus Mar 30 '16 at 16:05
  • I guess I just don't get it.. you want to know why you can send messages over the socket in the thread – ΦXocę 웃 Пepeúpa ツ Mar 30 '16 at 16:23
  • No, the socket just happened to be in my code. The question was really about who is executing what in the Thread. I think the answer by @djodjo got me thinking in the right direction. – Magnus Mar 30 '16 at 16:35