如何阻止网络I / O的线程阻塞?

时间:2012-10-27 01:31:09

标签: java multithreading

我目前正在尝试编写一个非常简单的聊天应用程序来介绍自己的java套接字编程和多线程。它由2个模块组成,一个伪服务器和一个伪服务器,但我的设计让我相信我正在尝试实现一个不可能的概念。

服务器

服务器在localhost端口4000上等待连接,当它收到连接时,它启动2个线程,一个监听器线程和一个扬声器线程。扬声器线程不断等待用户输入到控制台,并在接收到所述输入时将其发送到客户端。监听器线程阻塞客户端发送的任何消息的套接字的ObjectInputStream,然后将消息打印到控制台。

客户

客户端将用户连接到端口4000上的服务器,然后启动2个线程,一个监听器和一个扬声器。这些线程具有与服务器线程相同的功能,但出于显而易见的原因,以相反的方式处理输入/输出。

第一个问题

我遇到的问题是,为了结束聊天,用户必须输入“Bye”。现在,因为我的线程已经循环阻塞输入:

    while(connected()){

        //block for input
        //do something with this input
        //determine if the connection still exists (was the message "Bye"?)
    }

然后,在尝试退出应用程序时,这将成为一个非常有趣的场景。如果客户端键入“Bye”,则它返回发送线程,并且在服务器上侦听“Bye”的线程也返回。这给我们留下了一个问题,即客户端侦听器和服务器端扬声器不知道已经键入了“Bye”,因此继续执行。

我通过创建一个类Synchronizer来解决这个问题,该类包含两个线程以同步方式访问的布尔变量:

public class Synchronizer {

    boolean chatting;

    public Synchronizer(){

        chatting = true;
        onChatStatusChanged();
    }

    synchronized void stopChatting(){

        chatting = false;
        onChatStatusChanged();
    }

    synchronized boolean chatting(){

        return chatting;
    }

    public void onChatStatusChanged(){

     System.out.println("Chat status changed!: " + chatting);
    }
}

然后我将此类的相同实例传递给创建的线程。但仍有一个问题。

第二个问题

这是我推断出我正在尝试做的事情是不可能使用我目前使用的方法。鉴于一个用户必须键入“Bye”才能退出聊天,其他两个未被利用的线程仍会继续检查连接并开始阻止I / O.当它们阻塞时,原来的2个线程意识到连接已经终止,但即使它们改变了布尔值,其他2个线程已经已经通过了检查,并且已经阻塞了I / O.

这意味着即使您将在循环的下一次迭代中终止线程,您仍将尝试从已正确终止的其他线程接收输入。这导致我得出最后的结论和问题。

我的问题

是否可以以我正在尝试的方式异步接收和发送数据? (每个客户端/服务器有2个线程阻塞I / O)或者我必须每隔几毫秒在服务器和客户端之间来回发送心跳请求任何新数据并使用此心跳来确定断开连接?

问题似乎在于我的线程在他们意识到伙伴线程已断开连接之前阻塞了I / O.这导致了主要问题,那么你将如何异步停止线程阻塞I / O?

我觉得这应该能够完成,因为整个社交媒体都可以看到这种行为。

非常感谢任何澄清或建议!

2 个答案:

答案 0 :(得分:1)

我不懂Java,但如果它有线程,能够在线程上调用函数,并且能够终止线程,那么即使它没有任务,你也可以添加任务,这就是你的全部需要开始构建自己的ASync接口。

就此而言,如果你可以杀死线程,那么现有线程就可以杀死其他线程。

此外,在窗口关闭且连接打开的任何情况下都应发送“Bye”(或其他一些代码) - 如果Java有事件,并且您正在使用的窗口有Close事件,那么这是放置它的地方。

或者,您可以测试有效/打开的窗口,如果窗口无效/关闭,则发送“再见”。可以把它想象成一个穷人的事件处理程序。

另外,请确保您知道如何(并且有权限)手动向网络的防火墙添加例外。

此外,始终在实时网络上进行测试。仅仅因为它在环回中工作,并不意味着它将在网络上工作。虽然你可能已经知道了。

答案 1 :(得分:1)

为了澄清将来可能偶然发现这篇文章的人,我最后通过调整我的线程的语法来解决这个问题。首先,我必须删除旧线程,并分别用AsyncSender和AsyncReader替换它们。无论用户输入如何,这些线程始终发送和接收。当没有用户输入时,它只是发送/接收一个空白字符串,只有它是空白字符串才会打印到控制台。

解决方法

try{        
    if((obj = in.readObject()) != null){

    if(obj instanceof String)
        output = (String) obj;

    if(output.equalsIgnoreCase("Bye"))
    s.stop();
    }
}
catch(ClassNotFoundException e){

    e.printStackTrace();
}
catch(IOException e){

    e.printStackTrace();
}

在接收器线程的这个迭代中,它不会阻塞输入,而是测试对象读取是否为null(流中没有对象)。发送方线程也是如此。

这成功地绕过了必须停止阻塞I / O的线程的问题。

请注意,还有其他方法可以解决此问题,例如使用InterruptableChannel

相关问题