简体   繁体   English

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

[英]Linking two Threads in a Client-Server Socket program - Java

I create threads of class A and each sends a serialized object to a Server using ObjectOutputStream. 我创建了类A的线程,每个线程都使用ObjectOutputStream将序列化的对象发送到服务器

The Server creates new Threads B for each socket connection (whenever a new A client connects) 服务器为每个套接字连接创建新的线程B (每当新的A客户端连接时)

B will call a synchronized method on a Shared Resource Mutex which causes it (B) to wait() until some internal condition in the Mutex is true. B将在共享资源互斥对象上调用同步方法,这将导致它(B)进行wait()直到互斥对象中的某些内部条件为真。

In this case how A can know that B is currently waiting? 在这种情况下, A如何知道B目前正在等待?

Hope this description is clear. 希望这个描述清楚。

Class Arrangement: 班级安排:

A1--------->B1-------->|       |
A2--------->B2-------->| Mutex |
A3--------->B3-------->|       |

EDIT: it's a must to have wait(), notify() or notifyAll(), since this is for an academic project where concurrency is tested. 编辑:必须具有wait(),notify()或notifyAll(),因为这是针对测试并发性的学术项目。

Normally A would read on the socket, which would "block" (ie not return, hang up) until some data was sent back by B. It doesn't need to be written to deal with the waiting status of B. It just reads and that inherently involves waiting for something to read. 通常,A会在套接字上读取,这会“阻塞”(即不返回,挂断),直到B发出一些数据为止。不需要写它来处理B的等待状态。它只需读取这必然涉及等待阅读的内容。

Update So you want A's user interface to stay responsive. 更新因此,您希望A的用户界面保持响应。 By far the best way to do that is take advantage of the user interface library's event queue system. 到目前为止,最好的方法是利用用户界面库的事件队列系统。 All GUI frameworks have a central event loop that dispatches events to handlers (button click, mouse move, timer, etc.) There is usually a way for a background thread to post something to that event queue so that it will be executed on the main UI thread. 所有GUI框架都有一个中央事件循环,该循环将事件调度到处理程序(按钮单击,鼠标移动,计时器等)。通常,后台线程可以通过某种方式将某些内容发布到该事件队列中,以便在主线程上执行该事件。 UI线程。 The details will depend on the framework you're using. 详细信息将取决于您使用的框架。

For example, in Swing, a background thread can do this: 例如,在Swing中,后台线程可以执行以下操作:

SwingUtilities.invokeAndWait(someRunnableObject);

So suppose you define this interface: 因此,假设您定义此接口:

public interface ServerReplyHandler {
    void handleReply(Object reply);
}

Then make a nice API for your GUI code to use when it wants to submit a request to the server: 然后为要向服务器提交请求的GUI代码制作一个不错的API:

public class Communications {

    public static void callServer(Object inputs, ServerReplyHandler handler);

}

So your client code can call the server like this: 因此,您的客户端代码可以像这样调用服务器:

showWaitMessage();

Communications.callServer(myInputs, new ServerReplyHandler() {
    public void handleReply(Object myOutputs) {

        hideWaitMessage();
        // do something with myOutputs...

    }
});

To implement the above API, you'd have a thread-safe queue of request objects, which store the inputs object and the handler for each request. 要实现上述API,您将拥有一个请求对象的线程安全队列,该队列存储inputs对象和每个请求的处理程序。 And a background thread which just does nothing but pull requests from the queue, send the serialised inputs to the server, read back the reply and deserialise it, and then do this: 还有一个后台线程,它除了从队列中提取请求外什么也不做,将序列化的输入发送到服务器,读回答复并反序列化它,然后执行以下操作:

final ServerReplyHandler currentHandler = ...
final Object currentReply = ...

SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {

        currentHandler.handleReply(currentReply);

    }
});

So as soon as the background thread has read back the reply, it passes it back into the main UI thread via a callback. 因此,一旦后台线程回读了答复,它将通过回调将其传递回主UI线程。

This is exactly how browsers do asynchronous communication from JS code. 这正是浏览器通过JS代码进行异步通信的方式。 If you're familiar with jQuery, the above Communications.callServer method is the same pattern as: 如果您熟悉jQuery,则上面的Communications.callServer方法与以下模式相同:

showWaitMessage();

$.get('http://...', function(reply) {

    hideWaitMessage();

    // do something with 'reply' 
});

The only difference in this case is that you are writing the whole communication stack by hand. 在这种情况下,唯一的区别是您是手工编写整个通信堆栈。

Update 2 更新2

You asked: 您询问:

You mean I can pass "new ObjectOutputStream().writeObject(obj)" as "myInputs" in Communications.callServer? 您是说我可以在Communications.callServer中将“ new ObjectOutputStream()。writeObject(obj)”作为“ myInputs”传递?

If all information is passed as serialised objects, you can build the serialisation into callServer . 如果所有信息都作为序列化对象传递,则可以将序列化构建到callServer The calling code just passes some object that supports serialisation. 调用代码只是传递一些支持序列化的对象。 The implementation of callServer would serialise that object into a byte[] and post that to the work queue. callServer的实现会将该对象序列化为byte[]并将其发布到工作队列。 The background thread would pop it from the queue and send the bytes to the server. 后台线程会将其从队列中弹出,然后将字节发送到服务器。

Note that this avoids serialising the object on the background thread. 请注意,这避免了在后台线程上序列化对象。 The advantage of this is that all background thread activity is separated from the UI code. 这样做的好处是所有后台线程活动都与UI代码分开。 The UI code can be completely unaware that you're using threads for communication. UI代码可能完全不知道您正在使用线程进行通信。

Re: wait and notify , etc. You don't need to write your own code to use those. 回复: waitnotify ,等等。您无需编写自己的代码即可使用这些代码。 Use one of the standard implementations of the BlockingQueue interface. 使用BlockingQueue接口的标准实现之一。 In this case you could use LinkedBlockingQueue with the default constructor so it can accept an unlimited number of items. 在这种情况下,您可以将LinkedBlockingQueue与默认构造函数一起使用,以便它可以接受无限数量的项目。 That means that submitting to the queue will always happen without blocking. 这意味着提交到队列将始终发生而不会阻塞。 So: 所以:

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);
}

Meanwhile the background worker thread is doing this: 同时,后台工作线程正在执行此操作:

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()
            );
        }
    });
}

The shutdown variable is just: shutdown变量只是:

private static Request shutdown = new Request();

ie it's a dummy request used as a special signal. 即,它是一个虚拟请求,用作特殊信号。 This allows you to have another public static method to allow the UI to ask the background thread to quit (would presumably clear the queue before putting shutdown on it). 这使您可以使用另一种公共静态方法,以允许UI要求退出后台线程(大概在shutdown队列之前先清除队列)。

Note the essentials of the pattern: UI objects are never accessed on the background thread. 请注意该模式的要点:永远不要在后台线程上访问UI对象。 They are only manipulated from the UI thread. 它们仅从UI线程进行操作。 There is a clear separation of ownership. 所有权明显分开。 Data is passed between threads as byte arrays. 数据在线程之间作为字节数组传递。

You could start multiple workers if you wanted to support more than one request happening simultaneously. 如果您想支持同时发生的多个请求,则可以启动多个工作人员。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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