我现在经历了很多关于同步的教程,我的头脑正在旋转。我从未真正理解它:(。
我有一个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;
}
这真的很简单吗?我知道这可能已经被问到并得到了回答,但是如果没有个人的帮助,我似乎没有得到它。
答案 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
}
如果您想要一个统一的频道说输出,那么您必须按照指示进行同步,以避免出现意外结果。
有两种简单的方法可以做到这一点: