线程在Java聊天应用程序中的行为不符合我的要求

时间:2014-04-23 15:09:21

标签: java multithreading

我正在开发一个聊天应用程序,并且我使用全局变量作为一种允许服务器线程访问和更新共享信息的方法(我知道这不是推荐的,但是我&#39 ;我暂时坚持使用这种方法。)

我想要发生的是,每次提交新邮件时,它都会打印到所有客户端,以便所有客户端都能看到它们。

实际发生的是,它只会更新我已提交消息的客户端。虽然它有点接收来自其他客户端的消息。它并不是同时发生的。

我哪里错了?

class Server {

public static void main(String argv[]) throws Exception { 

    boolean listening = true;
    int portNumber = 6000;

    try (ServerSocket serverSocket = new ServerSocket(portNumber)) { 
        while (listening) {
            new ServerThread(serverSocket.accept()).start();
        }
    } catch (IOException e) {
        System.err.println("Could not listen on port " + portNumber);
        System.exit(-1);
    }
}
}

用于处理客户端的服务器线程。

public class ServerThread extends Thread {
private Socket socket = null;
public ServerThread(Socket socket) {
    super("ServerThread");
    this.socket = socket;
}

public void run () {

    int msgCnt = 0;
    boolean running = true;

    try (
        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(
            new InputStreamReader(
                socket.getInputStream()));
    ) {
        String inputLine, outputLine;

        while(running) {



        while ((inputLine = in.readLine()) != null) {
            if (Global.messageCount > msgCnt){
                out.println(Global.currentMessage + " received from others");
                msgCnt = msgCnt + 1;
            }
            outputLine = inputLine;
            Global.currentMessage = inputLine;
            Global.messageCount = Global.messageCount + 1;
            //out.println(outputLine);


        }
            //if (outputLine.equals("QUIT"))
            //    break;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

此外,客户端代码

class Client {
public static void main(String argv[]) throws Exception { 
    String sentMessage;  //variable for input
    String status;
    boolean running;

    BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));

    Socket clientSocket = new Socket("127.0.0.1", 6000); //name of computer to connect with and port number to use

    DataOutputStream outToServer = 
            new DataOutputStream(clientSocket.getOutputStream());
    BufferedReader inFromServer = 
            new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

    running = true;

    while(running)
    {
    sentMessage = inFromUser.readLine(); //user inputs text to variable 'sentMessage'

    outToServer.writeBytes(sentMessage + '\n'); //the variable is sent to the server

    status = inFromServer.readLine(); //the modified data is returned from the server

    System.out.println("FROM SERVER: " + status); //display to user

    }     
clientSocket.close();     
}
}

全球类,只是几个变量

public class Global {
    public static String currentMessage = "Nothing";
    public static int messageCount = 0;
}

3 个答案:

答案 0 :(得分:1)

我认为你的错误就在这里:

    while ((inputLine = in.readLine()) != null) {
        if (Global.messageCount > msgCnt){
            out.println(Global.currentMessage + " received from others");
            msgCnt = msgCnt + 1;
        }
        outputLine = inputLine;
        Global.currentMessage = inputLine;
        Global.messageCount = Global.messageCount + 1;
    }

请注意,一旦客户端提交了消息,您才会开始执行此while循环的内容。如果线程在in.readLine()上被阻塞而另一个线程提交了一条消息,那么没有任何东西可以导致这个while循环开始。我希望你能在这个客户提交的下一条消息上得到你想要的输出。

另一个注意事项:您似乎应该设置msgCnt = Global.messageCount,否则您可能会遇到Global.messageCount超过当前+1 msgCnt的情况,然后你永远运行你的if (Global.messageCount > msgCnt)陈述的内容。

答案 1 :(得分:0)

您需要维护所有客户的列表。

如果从任何客户端收到任何消息,只需遍历列表并将消息发送给其他客户端。

示例代码:

List<ServerThread> allClients=new ArrayList<ServerThread>();

class ServerThread extends Thread {
   private Socket socket = null;
   private PrintWriter out;

    public ServerThread(Socket socket) {
        ...
        allClients.add(this);
    }
   ...

       ...
       //code where you received any message
       for (ServerThread client : allClients) {
             if(client != this) {
                  client.out.println(Global.currentMessage + " received from others");
             }
       }
       ...
}

我在相同的上下文中发布了示例代码。

请查看Java Server with Multiclient communication.并按照帖子查找更多样本。

答案 2 :(得分:0)

来自Brian Goetz的 Java Concurrency in Practice

  

[...]在没有同步的情况下,线程可能无法立即(或曾经)看到另一个线程中的操作结果有很多原因。编译器可以以与源代码建议的“明显”顺序不同的顺序生成指令,或者将变量存储在寄存器中而不是存储器中;处理器可以并行或不按顺序执行指令;缓存可能会改变将变量写入主内存的顺序;存储在处理器本地缓存中的值可能对其他处理器不可见。这些因素可以防止线程看到变量的最新值,并且可能导致其他线程中的内存操作看起来不按顺序发生 - 如果您没有使用足够的同步。

Global.currentMessage声明为volatile。另外,对Global.messageCount使用AtomicInteger,调用AtomicInteger.getAndIncrement()来增加计数器。

您还将在设计中遇到其他与并发相关的问题。我建议在继续之前阅读Java中的并发性。上述书籍是最优秀的资源。