如何正确停止一个正在等待stdIn输入的线程?

5
我已经实现了一个简单的聊天应用程序,它可以接收消息并将它们重定向到所有用户,因为我正在学习并行编程。
客户端有两个线程:一个用于监听服务器,另一个(主线程)用于向服务器写入。
这些是用于实现此操作的代码:
主线程:
    try {
        Thread listener = new Thread(new Receiver(in));
        listener.start();
        while (listener.isAlive()) {
            out.println(stdIn.readLine());
        }
        out.close();
        in.close();
        clientSocket.close();
    } catch (IOException e) {
        System.err.println("Error trying to send text to server" + e.getMessage());
    }

第二个线程的可运行对象:

public class Receiver implements Runnable {

    private BufferedReader in;

    public Receiver(BufferedReader serverInput) {
        this.in = serverInput;
    }

    @Override
    public void run() {
        String responseLine;
        try {
            while ((responseLine = in.readLine()) != null) {
                System.out.println(responseLine);

                if (responseLine.contains("Bye")) break;
            }
        } catch (IOException e) {
                System.err.println("Erro ao receber mensagem do servidor: " + e.getMessage());
        }
    }
}

客户端应该发送一条消息(#quit)以退出聊天,服务器将用一条消息(Bye)作出回应,结束客户端应用程序。
如何在接收线程停止生命周期后停止主线程?
我使用了isAlive()方法来完成它,但是在用户发送#quit消息后,线程仍然存活,并且再次落在stdIn.readline()上,直到用户发送另一条消息,即使他已经断开连接。
我尝试实现另一个线程(称为sender)来向服务器发送消息,并在主线程上使用listener.join()来使用Thread.interrupt()中断sender线程,但sender线程仍然会落在stdIn.readline()方法上,我不知道如何正确地中断IO。
如果有人想阅读完整的代码,可以在这个 github页面中找到。
2个回答

2
这个问题没有简单的解决方案,因为从流中读取数据是一个阻塞操作。EHCache/Terracotta 中实现的一个技巧是使用两个线程而不是一个,其中一个线程将从流中实际读取数据(Callable),而另一个线程将设置一个执行器服务并提交第一个线程的任务(使用 Future)。
这篇文章提供了详细的描述。

2
@Override
public void run() {
    String responseLine;
    try {
        while(!isInterrupted()) 
        while ((responseLine = in.readLine()) != null) {
            System.out.println(responseLine);

            if (responseLine.contains("Bye")) break;
        }
    } catch (IOException e) {
            System.err.println("Erro ao receber mensagem do servidor: " + e.getMessage());
    }
}

从另一个线程中,调用interrupt()方法关闭该线程

例如,在打印stdInput之前,请检查它是否为“bye”,然后调用interrupt方法。


您还可以使用标志比如isRunning并调用方法shutdown()

//member variable 
 private static volatile boolean isRunning = true;

public static void shutdown()
    {
        running = false;
    }

    public void run()
    {
        while( running )
        {
            //do whatever.
        }

谢谢回答。我尝试从另一个线程调用interrupt()方法,但即使调用了中断,主线程仍在readline()上等待输入。基本上发生的情况是用户仍然需要发送最后一条无用消息才能退出。通过使用Thread.sleep()帮助解决了标志解决方案,它会休眠直到服务器响应,并在线程被中断时抛出异常。但我想这容易出错。 - Eduardo macedo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接