简体   繁体   English

在DataOutputStream上同步

[英]Synchronize on DataOutputStream

I have gone through so many tutorials on Synchronization now that my head is spinning. 我现在已经经历了很多关于同步的教程,我的头脑正在旋转。 I have never truly understood it :(. 我从来没有真正理解它:(。

I have a Java server( MainServer ), that when a client connects creates a new thread( ServerThread ) with a DataOutputStream. 我有一个Java服务器(MainServer),当客户端连接创建一个新的线程(ServerThread)与DataOutputStream联合。

The client talks to the ServerThread and the ServerThread responds. 客户端与ServerThread通信ServerThread响应。 Every now and then the MainServer will distribute a message to all clients utilizing each ServerThread's DataOutputStream object. 时不时的MainServer将分发消息利用每个ServerThread的 DataOutputStream类对象的所有客户。

I am quite certain that every now and then my issue is because both the MainServer and ServerThread are trying to send something to the client at the same time. 我敢肯定的是,每一个现在,然后我的问题是,因为无论是MainServerServerThread试图在同一时间送东西给客户端。 Therefore I need to lock on the DataOutputStream object. 因此,我需要锁定DataOutputStream对象。 For the life of me I cannot understand this concept any further. 对于我的生活,我无法进一步理解这个概念。 Every example I read is confusing. 我读到的每个例子都令人困惑。

What is the correct way to handle this? 处理这个问题的正确方法是什么?

ServerThread's send to client method: ServerThread的发送到客户端方法:

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

MainServer's distribute to all clients method: 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);  
    }
}

I am thinking I should have something like this in my ServerThread class. 我想我的ServerThread类中应该有这样的东西。

public synchronized DataOutputStream getCommandOut(){
    return commandOut;
}

Is it really that simple? 它真的那么简单吗? I know this has likely been asked and answered, but I don't seem to be getting it still, without individual help. 我知道这可能已经被问到并得到了回答,但如果没有个人的帮助,我似乎没有得到它。

If this were me..... 如果这是我.....

I would have a LinkedBlockingQueue on each client-side thread. 我会在每个客户端线程上有一个LinkedBlockingQueue Then, each time the client thread has a moment of idleness on the socket, it checks the queue. 然后,每次客户端线程在套接字上有空闲时,它都会检查队列。 If there's a message to send from the queue, it sends it. 如果有要从队列发送的消息,它会发送它。

Then, the server, if it needs to, can just add items to that queue, and, when the connection has some space, it will be sent. 然后,服务器,如果需要,可以只添加项目到该队列,并且,当连接有一些空间时,它将被发送。

Add the queue, have a method on the ServerThread something like: 添加队列,在ServerThread上有一个方法:

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

and then, on the socket side, have a loop that has a timeout-block on it, so that it breaks out of the socket if it is idle, and then just: 然后,在套接字方面,有一个在其上有超时块的循环,以便它在空闲时突破套接字,然后只是:

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

and you're done. 你完成了

The LinkedBlockingQueue will manage the locking and synchronization for you. LinkedBlockingQueue将为您管理锁定和同步。

You are on the right track. 你走在正确的轨道上。

Every statement modifying the DataOutputStream should be synchronized on this DataOutputStream so that it is not concurrently accessed (and thus do not have any concurrent modification): 修改DataOutputStream每个语句都应该在此DataOutputStreamsynchronized ,以便不会同时访问它(因此没有任何并发​​修改):

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

And: 和:

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

Just putting my 2 cents: 只需要我的2美分:

The way I implement servers is this: 我实现服务器的方式是这样的:

Each server is a thread with one task only: listening for connections. 每个服务器都是一个只有一个任务的线程:侦听连接。 Once it recognizes a connection it generates a new thread to handle the connection's input/output (I call this sub-class ClientHandler). 一旦识别出连接,它就会生成一个新线程来处理连接的输入/输出(我称之为子类ClientHandler)。

The server also keeps a list of all connected clients. 服务器还会保留所有已连接客户端的列表。

ClientHandlers are responsible for user-server interactions. ClientHandlers负责用户 - 服务器交互。 From here, things are pretty simple: 从这里开始,事情非常简单:

Disclaimer: there are no try-catches blocks here! 免责声明:这里没有尝试捕获块! add them yourself. 自己添加它们。 Of course you can use thread executers to limit the number of concurrent connections. 当然,您可以使用线程执行程序来限制并发连接的数量。

Server's run() method: 服务器的run()方法:

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

ClientHandler's ctor: ClientHandler的ctor:

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

ClientHandler's run() method: ClientHandler的run()方法:

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

and handle() method: handle()方法:

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

If you want a unified channel say for output then you'll have to synchronize it as instructed to avoid unexpected results. 如果您想要一个统一的通道说输出,那么您必须按照指示进行同步以避免意外结果。

There are 2 simple ways to do this: 有两种简单的方法可以做到这一点:

  1. Wrap every call to output in synchronized(this) block 在synchronized(this)块中包含每次调用输出
  2. Use a getter for output (like you did) with synchronized keyword. 使用getter输出(就像你做的那样)使用synchronized关键字。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM