我创建了类 A 的线程,并使用ObjectOutputStream将序列化对象发送到服务器。
服务器为每个套接字连接创建新的主题 B (每当新的 A 客户端连接时)
B 会在共享资源 Mutex 上调用同步方法,导致它(B)等待(),直到某些内部条件为止互斥是真的。
在这种情况下, A 如何知道 B 目前正在等待?
希望这个描述很清楚。
课程安排:
A1--------->B1-------->| |
A2--------->B2-------->| Mutex |
A3--------->B3-------->| |
修改 拥有wait(),notify()或notifyAll()是必须的,因为这是针对测试并发性的学术项目。
答案 0 :(得分:4)
通常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
你问:
你的意思是我可以传递“new ObjectOutputStream()。writeObject(obj)”as Communications.callServer中的“myInputs”?
如果所有信息都作为序列化对象传递,则可以将序列化构建到callServer
。调用代码只传递一些支持序列化的对象。 callServer
的实现会将该对象序列化为byte[]
并将其发布到工作队列中。后台线程将从队列中弹出它并将字节发送到服务器。
请注意,这可以避免在后台线程上序列化对象。这样做的好处是所有后台线程活动都与UI代码分开。 UI代码可能完全没有意识到您正在使用线程进行通信。
Re: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线程操纵。所有权明显分离。数据在线程之间作为字节数组传递。
如果您想同时支持多个请求,则可以启动多个工作人员。