当前位置: 首页 > 知识库问答 >
问题:

在线程上调用一个方法-这是可行的,但为什么呢?

夹谷斌蔚
2023-03-14

我有以下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()不会阻止我这样做?我的意思是线程不应该一直忙于做其他事情吗?这是做事的好方法吗?如果不是,为什么不呢?

编辑

为了澄清我的困惑:如果这是在主线程中,它将在中的上繁忙。readObject()直到读取了某个内容,然后它将在循环的下一次迭代中再次开始侦听。当然,我可以从另一个线程调用send()。但让我大吃一惊的是,“谁”实际上在执行send()?是我的线程在做,还是调用线程在做?如果是前者,它怎么可能既忙着在while循环中等待输入,又忙着执行send()方法呢?我只是很难把我的思想集中在它上面。。。

共有3个答案

赫连秦迟
2023-03-14

这是有效的,因为在SWING/GUI中,您正在执行以下操作:

ConnectionHandlerThread myThread = ConnectionHandlerThread(new Socket());

现在,send()方法是公共的,所以您可以在执行此操作时从GUI类调用它

myThread.send(data);

诀窍在于,您在线程中使用了一个套接字,这都是因为您在类的构造函数中传递了一个套接字

杨安歌
2023-03-14

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

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

一位作者说,“编写对象太少的程序很容易。编写对象太多的程序很难。”

我将重新编写您的代码,以使用三个不同的对象来执行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();
        ...
    }
    ...
}

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

listener对象定义了线程的作用:即,它从连接接收对象,直到! isConnect()并对它们执行某些操作。

最后,connectionThread对象是启动线程的对象(可以用来等待线程完成等),如果需要的话。

它比你写的代码更多,但IMO,它更容易理解,因为它更容易看到每个单独部分的职责。有时,尤其是如果您与其他开发人员合作,使代码易于理解比使其变小更重要。

薛墨一
2023-03-14

有两件事:

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

输出:

无限阅读::⇢ ()[线程:“main”]无限线程::⇠ [0ms]无限读取:⇢ run()[线程:“Thread-0”]无限线程::⇢ fromIn()[线程:“线程-0”]无限线程::⇢ fromOut(数据=0)[线程:“main”]无限读取::⇠ fromOut[500ms]无穷大读数:⇢ fromOut(data=1)[线程:“main”]无限读取::⇠ fromIn【1000ms】无穷大读数:⇢ fromIn()[线程:“线程-0”]无限线程::⇠ fromOut[500ms]无穷大读数:⇢ fromOut(数据=2)[线程:“main”]无限读取::⇠ fromOut[500ms]无穷大读数:⇢ fromOut(数据=3)[线程:“main”]无限读取::⇠ fromIn【1000ms】无穷大读数:⇢ fromIn()[线程:“线程-0”]无限线程::⇠ fromOut[500ms]无穷大读数:⇢ fromOut(数据=4)[线程:“main”]无限读取::⇠ fromOut[500ms]无穷大读数:⇢ fromOut(数据=5)[线程:“main”]无限读取::⇠ fromIn【1000ms】无穷大读数:⇢ fromIn()[线程:“线程-0”]无限线程::⇠ fromOut[500ms]无穷大读数:⇢ fromOut(数据=6)[线程:“main”]无限读取::⇠ fromOut[500ms]无穷大读数:⇢ fromOut(数据=7)[线程:“main”]无限读取::⇠ fromIn【1000ms】无穷大读数:⇢ fromIn()[线程:“线程-0”]无限线程::⇠ fromOut[500ms]无穷大读数:⇢ fromOut(数据=8)[线程:“main”]无限读取::⇠ fromOut[500ms]无穷大读数:⇢ fromOut(数据=9)[线程:“main”]无限读取::⇠ fromIn【1000ms】无穷大读数:⇢ fromIn()[线程:“线程-0”]无限线程::⇠ fromOut[500ms]无穷大读数:⇠ fromIn【1000ms】无穷大读数:⇢ fromIn()[线程:“线程-0”]无限线程::⇠ fromIn【1000ms】无穷大读数:⇢ fromIn()[线程:“线程-0”]无限线程::⇠ fromIn【1000ms】无穷大读数:⇢ fromIn()[线程:“线程-0”]无限线程::⇠ fromIn【1000ms】无穷大读数:⇢ fromIn()[线程:“线程-0”]无限线程::⇠ fromIn【1000ms】无穷大读数:⇠ 运行【10003ms】

 类似资料:
  • 问题内容: 有没有简单的解决方案,可以在新线程中使用JPA将数据保存到数据库中? 我基于Spring的Web应用程序允许用户管理计划的任务。在运行时,他可以创建和启动预定义任务的新实例。我正在使用spring的TaskScheduler,并且一切正常。 但是我需要将每个被激发任务的布尔结果保存到数据库中。我怎样才能做到这一点? 编辑:我必须概括我的问题:我需要从任务在我的@Service类上调用方

  • 然而,在我下面的代码中,我希望在这两个示例中都花费相同的15秒(每个任务5秒),如本文所述。然而,第二个示例只需要5秒,同时运行所有3个示例也需要5秒来完成第二个示例。原来的文章花了5秒,但我把它改成了1秒的延迟,让它更明显。 有没有人能解释一下这是怎么回事,为什么它看起来像线程一样运行?

  • 我目前正在深入研究多线程,但我在理解我当前在代码中做错了什么方面遇到了一些问题。 我试图通过调用我的“ThreadPopulateList()”来用随机数据填充2个列表,在我的理解中,这应该以Parralel启动2个线程。因为我调用了该方法两次。 但当我调用ThreadMethod时,执行时间会增加50%。这是为什么?如有任何帮助,我们将不胜感激!

  • 本文向大家介绍请你来说一说Redis是单线程的,但是为什么这么高效呢?相关面试题,主要包含被问及请你来说一说Redis是单线程的,但是为什么这么高效呢?时的应答技巧和注意事项,需要的朋友参考一下 参考回答: 虽然Redis文件事件处理器以单线程方式运行,但是通过使用I/O多路复用程序来监听多个套接字,文件事件处理器既实现了高性能的网络通信模型,又可以很好地与Redis服务器中其他同样以单线程运行的

  • 例如: 假设有一个按钮启动异步请求,该请求返回并触发主线程上运行的runnable/callback。会发生什么?runnable被添加到MessageQueue中,并在“时间”到时运行。但是什么时候是“时间”呢?如果在异步请求将runnable发布到MainThread之前,我按下另一个按钮,在MainThread上执行一些半长的阻塞任务呢?它会等到我的阻塞按钮上的逻辑完成吗?会打断它吗?它是否

  • 这是我的密码 结果为上述结果之一。从调用仍然调用子类的,即使从词汇上讲,meth调用在超类内部!那么,当是私有的时候,为什么会有不同的行为呢? ___________edit____________ 代码是这样的吗 O/P将是 因此,即使超级类中的正在调用,但实际上子类的正在被调用。所以,方法调用不是词法意义上的!也就是说,即使看起来将调用超级类的meth,实际上它的子类的becoz子类实例首先调