Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在DataOutputStream上同步_Java_Multithreading_Synchronization_Dataoutputstream - Fatal编程技术网

Java 在DataOutputStream上同步

Java 在DataOutputStream上同步,java,multithreading,synchronization,dataoutputstream,Java,Multithreading,Synchronization,Dataoutputstream,我已经学习了很多关于同步的教程,现在我的头都快晕过去了。我从来没有真正理解过 我有一个Java服务器(MainServer),当客户端连接时,它会创建一个带有DataOutputStream的新线程(ServerThread) 客户端与ServerThread对话,ServerThread响应。每隔一段时间,MainServer会利用每个ServerThread的DataOutputStream对象向所有客户端分发一条消息 我很确定,我时不时会遇到的问题是因为主服务器和服务器线程都试图同时向客户

我已经学习了很多关于同步的教程,现在我的头都快晕过去了。我从来没有真正理解过

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

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

我很确定,我时不时会遇到的问题是因为主服务器和服务器线程都试图同时向客户端发送一些东西。因此,我需要锁定DataOutputStream对象。就我的一生而言,我无法进一步理解这个概念。我读到的每个示例都是confusing

正确的处理方法是什么

服务器线程的发送到客户端方法:

public void replyToOne(String reply){
    try {
        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 {
            temporaryCOut.writeUTF(broadcastMessage);
            temporaryCOut.flush();
        } catch (IOException e) {
            logger.error("distribute: writeUTF", e);
        }
        logger.info(broadcastMessage);  
    }
}
MainServer的“分发到所有客户端”方法:

public void replyToOne(String reply){
    try {
        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 {
            temporaryCOut.writeUTF(broadcastMessage);
            temporaryCOut.flush();
        } catch (IOException e) {
            logger.error("distribute: writeUTF", e);
        }
        logger.info(broadcastMessage);  
    }
}
真的那么简单吗?我知道这很可能已经被问到和回答了,但如果没有个人的帮助,我似乎还没有得到答案。

如果这是我

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

然后,服务器(如果需要的话)只需将项目添加到该队列中,当连接有一些空间时,它就会被发送

添加队列,在ServerThread上创建一个方法,如下所示:

addBroadcastMessage(MyData data) {
    broadcastQueue.add(data);
}
然后,在套接字端,有一个在其上有超时块的循环,这样,如果它空闲,它就会从套接字中断,然后:

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


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

您走在正确的轨道上

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

以及:

公共静态无效分发(字符串广播消息){
对于(Map.Entry:AccountInfoList.entrySet()){
对象[]tmpObjArray=entry.getValue();
DataOutputStream temporaryCOut=(DataOutputStream)tmpObjArray[INT_COMMAND_OUT];//可以在线程使用它时抓取
试一试{
已同步(临时cout){//写入块
临时cout.writeUTF(广播消息);
暂时性cout.flush();
}
}捕获(IOE异常){
记录器错误(“分发:writeUTF”,e);
}
logger.info(广播消息);
}
}

只要放上我的2美分:

我实现服务器的方式如下:

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

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

ClientHandler负责用户与服务器的交互。从这里,事情非常简单:

免责声明:这里没有try-catch块!请自己添加它们。当然,您可以使用线程执行器来限制并发连接的数量

服务器的
run()
方法:

@Override
public void run(){
 isRunning = true;
 while(isRunning){
  ClientHandler ch = new ClientHandler(serversocket.accept());
  clients.add(ch);
  ch.start();
 }
}
@Override
public void run(){
 isConnected = true;
 while(isConnected){
  handle(in.readObject());
 }
}
private void handle(Object o){
 //Your implementation
}
ClientHandler's ctor:

public ClientHandler(Socket client){
 out = new ObjectOutputStream(client.getOutputStream());
 in = new ObjectInputStream(client.getInputStream());
}
ClientHandler的
run()
方法:

@Override
public void run(){
 isRunning = true;
 while(isRunning){
  ClientHandler ch = new ClientHandler(serversocket.accept());
  clients.add(ch);
  ch.start();
 }
}
@Override
public void run(){
 isConnected = true;
 while(isConnected){
  handle(in.readObject());
 }
}
private void handle(Object o){
 //Your implementation
}
handle()
方法:

@Override
public void run(){
 isRunning = true;
 while(isRunning){
  ClientHandler ch = new ClientHandler(serversocket.accept());
  clients.add(ch);
  ch.start();
 }
}
@Override
public void run(){
 isConnected = true;
 while(isConnected){
  handle(in.readObject());
 }
}
private void handle(Object o){
 //Your implementation
}
如果您想要一个统一的通道,比如说输出,那么您必须按照指示对其进行同步,以避免意外的结果

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

  • 在synchronized(this)块中包装对输出的每个调用
  • 使用getter作为synchronized关键字的输出(就像您所做的那样)

  • 这就是您的问题:主服务器会不时地向使用每个ServerThread的DataOutputStream对象的所有客户端分发一条消息……为什么?为什么不在ServerThread实例上调用一个方法,然后让ServerThread在适当的时间将消息发送给客户端……?同意@rolfl,我强烈建议你这样读可以解决这个问题,因为ServerThread是对DataOutputStream的单线程访问…这一点很好。我在这里发毛,它真的那么简单吗?@shazin谢谢你,我会好好读一读,希望它能帮助我真正理解它。现在根据rolfl的建议,避免多线程访问是合乎逻辑的,但我相信我将来需要知道。谢谢你们两位的帮助。非常好,已经将一些问题转到了这里,并抽查了我的工作…工作非常有魅力。我将完成其余的工作。这将使将来的调试更简单。将指定的“服务器线程”的所有内容发送到该客户端将使调试更简单。再次感谢您的帮助!啊,这终究是行不通的…我的服务器线程的commandIn.readUTF()将阻止,因此即使队列获得添加到其中的项目,它也将首先等待客户端请求消息,然后再耗尽循环顶部的队列消息。是否有任何方法使向队列添加消息取消阻止commandIn.readUTF()?Jean非常感谢您的洞察力。我真的很感激。这有助于我减少一点失落感。为了让它尽可能简单,我将避免使用多线程,而只使用队列解决方案,因为我的每个客户端都有一个线程。尽管如此,这仍然让我对自己的掌握更有信心。:)这个设置与我的设置几乎相同。我只是为了我的问题简化了。MyServer(带有connectionHandler线程的大脑),每个连接都会产生MyCommandHandler线程,并向客户端提供输入/输出流。当我的服务器