Reputation: 18768
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...
Upvotes: 0
Views: 94
Reputation: 27125
@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.
Upvotes: 1
Reputation: 3576
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]
Upvotes: 1
Reputation: 48288
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
Upvotes: -1