在线程上调用方法 - 它有效,但为什么呢?

时间:2016-03-30 15:02:48

标签: java multithreading swing runnable java-threads

我有以下Thread子类(为了便于阅读,略有简化):

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
        }       
    }
}

当我连接到服务器时,我的客户端(使用Swing GUI)创建了此线程的实例。我觉得奇怪的是我可以从主GUI调用方法send(Object data),它可以工作。为什么无限循环和/或对in.readObject()的调用不能阻止我这样做?我的意思是不应该线程忙于不断做其他事情?这是一个做事的好方法吗?如果没有,为什么不呢?

修改

澄清一下让我感到困惑的是:如果这是在主线程中,它会忙于in.readObject()直到读取某些东西,然后它才会在循环的下一次迭代中再次开始监听。当然,我知道我可以从另一个线程调用send()。但是我的思绪在哪里 - “谁”实际上正在执行send()?我的Thread是在做它,还是调用线程呢?如果是前者,那么如何在while循环执行send()方法时忙于等待输入?我只是很难把它包裹起来......

3 个答案:

答案 0 :(得分:1)

有两件事:

1)无限循环不会使cpu只与自身保持忙碌。它只是在它可用的时候保持忙碌,但我也使用其他线程。

2)当你打电话给

  

发送(对象数据)

你没有从你的线程中做到这一点,因此请记住1)没有什么奇怪的,它被称为

示例:

代码:

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();
            }
        }
    }
}

输出:

  

InfiniteThread ::⇢()[线程:“main”] InfiniteThread ::⇠    [0ms] InfiniteThread ::⇢run()[线程:“Thread-0”]   InfiniteThread ::⇢fromIn()[Thread:“Thread-0”] InfiniteThread ::⇢   fromOut(data = 0)[线程:“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)[线程:“主要”] InfiniteThread ::⇠fromOut[500ms]   InfiniteThread ::⇢fromOut(data = 3)[Thread:“main”] InfiniteThread ::   ⇠fromIn[1000ms] InfiniteThread ::⇢fromIn()[Thread:“Thread-0”]   InfiniteThread ::⇠fromOut [500ms] InfiniteThread ::⇢   fromOut(data = 4)[线程:“主要”] InfiniteThread ::⇠fromOut[500ms]   InfiniteThread ::⇢fromOut(data = 5)[Thread:“main”] InfiniteThread ::   ⇠fromIn[1000ms] InfiniteThread ::⇢fromIn()[Thread:“Thread-0”]   InfiniteThread ::⇠fromOut [500ms] InfiniteThread ::⇢   fromOut(data = 6)[线程:“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)[线程:“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-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]

答案 1 :(得分:1)

  

@jameslarge是的,我现在很困惑......

Thread对象不是线程:它可以用于配置,创建和与线程交互的方法。您正在使用ConnectionHandlerThread来完成所有这些,并且您也在使用它(通过覆盖run()方法)来定义线程所做的工作,并且您也在使用它(由几个private fields)表示线程操作的连接状态。这有很多超载。

一位作者说过,#34;编写一个对象太少的程序很容易。编写一个包含太多对象的文件很难。&#34;

我会重新编写代码,使用三个不同的对象来完成ConnectionHandlerThread现在执行的三个不同的工作:

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();
        ...
    }
    ...
}

connection对象知道如何发送和接收数据。

listener对象定义了线程的作用:即,它从连接接收对象直到! isConnected()并且它对它们做了一些事情。

最后,connectionThread对象是启动线程的原因(可以用来等待它完成等)。

它比您编写的代码更多,但是IMO,它更容易理解,因为它更容易看到每个单独作品的责任。有时,特别是如果您与其他开发人员合作,使代码易于理解比将其缩小更重要。

答案 2 :(得分:-1)

这是有效的,因为在SWING / GUI中你正在做类似的事情:

ConnectionHandlerThread myThread = ConnectionHandlerThread(new Socket());

现在send()方法是公共的,因此您可以在执行

时从GUI类中调用它
myThread.send(data);

诀窍是你在Thread中使用套接字,这都是因为你在类ConnectionHandlerThread的构造函数中传递了一个套接字