繁体   English   中英

在客户端服务器套接字程序中链接两个线程-Java

[英]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代码可能完全不知道您正在使用线程进行通信。

回复: waitnotify ,等等。您无需编写自己的代码即可使用这些代码。 使用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.

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