[英]Linking two Threads in a Client-Server Socket program - Java
我创建了类A的线程,每个线程都使用ObjectOutputStream将序列化的对象发送到服务器 。
服务器为每个套接字连接创建新的线程B (每当新的A客户端连接时)
B将在共享资源互斥对象上调用同步方法,这将导致它(B)进行wait()直到互斥对象中的某些内部条件为真。
在这种情况下, A如何知道B目前正在等待?
希望这个描述清楚。
班级安排:
A1--------->B1-------->| |
A2--------->B2-------->| Mutex |
A3--------->B3-------->| |
编辑:必须具有wait(),notify()或notifyAll(),因为这是针对测试并发性的学术项目。
通常,A会在套接字上读取,这会“阻塞”(即不返回,挂断),直到B发出一些数据为止。不需要写它来处理B的等待状态。它只需读取这必然涉及等待阅读的内容。
更新因此,您希望A的用户界面保持响应。 到目前为止,最好的方法是利用用户界面库的事件队列系统。 所有GUI框架都有一个中央事件循环,该循环将事件调度到处理程序(按钮单击,鼠标移动,计时器等)。通常,后台线程可以通过某种方式将某些内容发布到该事件队列中,以便在主线程上执行该事件。 UI线程。 详细信息将取决于您使用的框架。
例如,在Swing中,后台线程可以执行以下操作:
SwingUtilities.invokeAndWait(someRunnableObject);
因此,假设您定义此接口:
public interface ServerReplyHandler {
void handleReply(Object reply);
}
然后为要向服务器提交请求的GUI代码制作一个不错的API:
public class Communications {
public static void callServer(Object inputs, ServerReplyHandler handler);
}
因此,您的客户端代码可以像这样调用服务器:
showWaitMessage();
Communications.callServer(myInputs, new ServerReplyHandler() {
public void handleReply(Object myOutputs) {
hideWaitMessage();
// do something with myOutputs...
}
});
要实现上述API,您将拥有一个请求对象的线程安全队列,该队列存储inputs
对象和每个请求的处理程序。 还有一个后台线程,它除了从队列中提取请求外什么也不做,将序列化的输入发送到服务器,读回答复并反序列化它,然后执行以下操作:
final ServerReplyHandler currentHandler = ...
final Object currentReply = ...
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
currentHandler.handleReply(currentReply);
}
});
因此,一旦后台线程回读了答复,它将通过回调将其传递回主UI线程。
这正是浏览器通过JS代码进行异步通信的方式。 如果您熟悉jQuery,则上面的Communications.callServer
方法与以下模式相同:
showWaitMessage();
$.get('http://...', function(reply) {
hideWaitMessage();
// do something with 'reply'
});
在这种情况下,唯一的区别是您是手工编写整个通信堆栈。
更新2
您询问:
您是说我可以在Communications.callServer中将“ new ObjectOutputStream()。writeObject(obj)”作为“ myInputs”传递?
如果所有信息都作为序列化对象传递,则可以将序列化构建到callServer
。 调用代码只是传递一些支持序列化的对象。 callServer
的实现会将该对象序列化为byte[]
并将其发布到工作队列。 后台线程会将其从队列中弹出,然后将字节发送到服务器。
请注意,这避免了在后台线程上序列化对象。 这样做的好处是所有后台线程活动都与UI代码分开。 UI代码可能完全不知道您正在使用线程进行通信。
回复: wait
并notify
,等等。您无需编写自己的代码即可使用这些代码。 使用BlockingQueue接口的标准实现之一。 在这种情况下,您可以将LinkedBlockingQueue
与默认构造函数一起使用,以便它可以接受无限数量的项目。 这意味着提交到队列将始终发生而不会阻塞。 所以:
private static class Request {
public byte[] send;
public ServerReplyHandler handler;
};
private BlockingQueue<Request> requestQueue;
public static callServer(Object inputs, ServerReplyHandler handler) {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
new ObjectOutputStream(byteStream).writeObject(inputs);
Request r = new Request();
r.send = byteStream.toByteArray();
r.handler = handler;
requestQueue.put(r);
}
同时,后台工作线程正在执行此操作:
for (;;) {
Request r = requestQueue.take();
if (r == shutdown) {
break;
}
// connect to server, send r.send bytes to it
// read back the response as a byte array:
byte[] response = ...
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
currentHandler.handleReply(
new ObjectInputStream(
new ByteArrayInputStream(response)
).readObject()
);
}
});
}
shutdown
变量只是:
private static Request shutdown = new Request();
即,它是一个虚拟请求,用作特殊信号。 这使您可以使用另一种公共静态方法,以允许UI要求退出后台线程(大概在shutdown
队列之前先清除队列)。
请注意该模式的要点:永远不要在后台线程上访问UI对象。 它们仅从UI线程进行操作。 所有权明显分开。 数据在线程之间作为字节数组传递。
如果您想支持同时发生的多个请求,则可以启动多个工作人员。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.