在DataOutputStream上同步

时间:2014-10-09 02:06:09

标签: java multithreading synchronization dataoutputstream

我现在经历了很多关于同步的教程,我的头脑正在旋转。我从未真正理解它:(。

我有一个Java服务器( MainServer ),当客户端连接时,会创建一个带有DataOutputStream的新线程( ServerThread )。

客户端与 ServerThread 对话, ServerThread 响应。有时 MainServer 会使用每个 ServerThread的 DataOutputStream对象向所有客户端分发一条消息。

我很确定我的问题是因为 MainServer ServerThread 都在尝试同时向客户端发送内容。因此,我需要锁定DataOutputStream对象。对于我的生活,我无法进一步理解这个概念。我读到的每个例子都令人困惑。

处理此问题的正确方法是什么?

ServerThread的发送到客户端方法:

public void replyToOne(String reply){
    try {
        commandOut.writeUTF(reply);
        commandOut.flush();
    } catch (IOException e) {
        logger.fatal("replyToOne", e);
    }
    logger.info(reply);
}

MainServer分发给所有客户端方法:

public static void distribute(String broadcastMessage){
    for (Map.Entry<String, Object[]> entry : AccountInfoList.entrySet()) {
        Object[] tmpObjArray = entry.getValue();
        DataOutputStream temporaryCOut = (DataOutputStream) tmpObjArray[INT_COMMAND_OUT]; //can be grabbed while thread is using it
        try {
            temporaryCOut.writeUTF(broadcastMessage);
            temporaryCOut.flush();
        } catch (IOException e) {
            logger.error("distribute: writeUTF", e);
        }
        logger.info(broadcastMessage);  
    }
}

我想我的 ServerThread 类中应该有这样的东西。

public synchronized DataOutputStream getCommandOut(){
    return commandOut;
}

这真的很简单吗?我知道这可能已经被问到并得到了回答,但是如果没有个人的帮助,我似乎没有得到它。

3 个答案:

答案 0 :(得分:1)

如果这是我......

我会在每个客户端线程上有一个LinkedBlockingQueue。然后,每次客户端线程在套接字上有空闲时,它都会检查队列。如果有要从队列发送的消息,它会发送它。

然后,服务器,如果需要,可以只添加项目到该队列,并且,当连接有一些空间时,它将被发送。

添加队列,在ServerThread上有一个方法:

addBroadcastMessage(MyData data) {
    broadcastQueue.add(data);
}

然后,在套接字方面,有一个循环,其上有一个超时块​​,以便它在空闲时突破套接字,然后只是:

while (!broadcastQueue.isEmpty()) {
    MyData data = broadcastQueue.poll();
    .... send the data....
}

你已经完成了。

LinkedBlockingQueue将为您管理锁定和同步。

答案 1 :(得分:1)

你走在正确的轨道上。

DataOutputStream上修改synchronized的每个语句都应该是DataOutputStream,这样就不会同时访问它(因此没有任何并发​​修改):

public void replyToOne(String reply){
    try {
        synchronized(commandOut) {    // writing block
            commandOut.writeUTF(reply);
            commandOut.flush();
        }
    } catch (IOException e) {
        logger.fatal("replyToOne", e);
    }
    logger.info(reply);
}

public static void distribute(String broadcastMessage){
    for (Map.Entry<String, Object[]> entry : AccountInfoList.entrySet()) {
        Object[] tmpObjArray = entry.getValue();
        DataOutputStream temporaryCOut = (DataOutputStream) tmpObjArray[INT_COMMAND_OUT]; //can be grabbed while thread is using it
        try {
            synchronized(temporaryCOut) {  // writing block
                temporaryCOut.writeUTF(broadcastMessage);
                temporaryCOut.flush();
            }
        } catch (IOException e) {
            logger.error("distribute: writeUTF", e);
        }
    logger.info(broadcastMessage);  
    }
}

答案 2 :(得分:1)

只需要我的2美分:

我实现服务器的方式是:

每个服务器都是一个只有一个任务的线程:侦听连接。一旦识别出连接,它就会生成一个新线程来处理连接的输入/输出(我称之为子类ClientHandler)。

服务器还会保留所有已连接客户端的列表。

ClientHandlers负责用户 - 服务器交互。从这里开始,事情很简单:

免责声明:此处没有尝试捕获块!自己添加它们。当然,您可以使用线程执行器来限制并发连接的数量。

服务器的run()方法:

@Override
public void run(){
 isRunning = true;
 while(isRunning){
  ClientHandler ch = new ClientHandler(serversocket.accept());
  clients.add(ch);
  ch.start();
 }
}

ClientHandler&#39>:

public ClientHandler(Socket client){
 out = new ObjectOutputStream(client.getOutputStream());
 in = new ObjectInputStream(client.getInputStream());
}

ClientHandler的run()方法:

@Override
public void run(){
 isConnected = true;
 while(isConnected){
  handle(in.readObject());
 }
}

handle()方法:

private void handle(Object o){
 //Your implementation
}

如果您想要一个统一的频道说输出,那么您必须按照指示进行同步,以避免出现意外结果。

有两种简单的方法可以做到这一点:

  1. 在synchronized(this)块
  2. 中包含每次调用输出
  3. 使用getter输出(就像你一样)使用synchronized关键字。