Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/380.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在客户机-服务器套接字程序中链接两个线程-Java_Java_Multithreading_Sockets_Client Server_Mutex - Fatal编程技术网

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

在客户机-服务器套接字程序中链接两个线程-Java,java,multithreading,sockets,client-server,mutex,Java,Multithreading,Sockets,Client Server,Mutex,我创建类A的线程,每个线程使用ObjectOutputStream向服务器发送一个序列化对象 服务器为每个套接字连接创建新线程B(每当新的a客户端连接时) B将调用共享资源互斥体上的同步方法,这将导致它(B)等待(),直到互斥体中的某个内部条件为真 在这种情况下,A如何知道B当前正在等待 希望这个描述清楚 班级安排: A1--------->B1-------->| | A2--------->B2-------->| Mutex | A3---------&

我创建类A的线程,每个线程使用ObjectOutputStream向服务器发送一个序列化对象

服务器为每个套接字连接创建新线程B(每当新的a客户端连接时)

B将调用共享资源互斥体上的同步方法,这将导致它(B)等待(),直到互斥体中的某个内部条件为真

在这种情况下,A如何知道B当前正在等待

希望这个描述清楚

班级安排:

A1--------->B1-------->|       |
A2--------->B2-------->| Mutex |
A3--------->B3-------->|       |
编辑:
必须有wait()、notify()或notifyAll(),因为这是一个测试并发性的学术项目。

通常a会读取套接字,这将“阻塞”(即不返回、挂断)直到一些数据被B发回。它不需要被写入来处理B的等待状态。它只是读取,而这本质上涉及到等待读取

更新以便您希望A的用户界面保持快速响应。到目前为止,最好的方法是利用用户界面库的事件队列系统。所有GUI框架都有一个中央事件循环,它将事件分派给处理程序(按钮单击、鼠标移动、计时器等)。后台线程通常有一种方法将某些内容发布到该事件队列,以便在主UI线程上执行。细节将取决于您使用的框架

例如,在Swing中,后台线程可以执行以下操作:

SwingUtilities.invokeAndWait(someRunnableObject);
final ServerReplyHandler currentHandler = ...
final Object currentReply = ...

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

        currentHandler.handleReply(currentReply);

    }
});
因此,假设您定义此接口:

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,您需要一个线程安全的请求对象队列,该队列存储
输入
对象和每个请求的处理程序。还有一个后台线程,它什么也不做,只是从队列中提取请求,将序列化的输入发送到服务器,读回回复并反序列化,然后执行以下操作:

SwingUtilities.invokeAndWait(someRunnableObject);
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

你问:

你的意思是我可以将“newObjectOutputStream().writeObject(obj)”作为 Communications.callServer中的“myInputs”

如果所有信息都作为序列化对象传递,则可以将序列化构建到
callServer
中。调用代码只是传递一些支持序列化的对象。
callServer
的实现将该对象序列化为
字节[]
,并将其发布到工作队列。后台线程将从队列中弹出它,并将字节发送到服务器

请注意,这样可以避免在后台线程上序列化对象。这样做的好处是所有后台线程活动都与UI代码分离。UI代码可能完全不知道您正在使用线程进行通信

回复:
等待
通知
,等等。您不需要编写自己的代码来使用它们。使用接口的标准实现之一。在这种情况下,您可以使用带有默认构造函数的
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);
}
shutdown
变量仅为:

private static Request shutdown = new Request();
i、 这是一个用作特殊信号的虚拟请求。这允许您使用另一个公共静态方法来允许UI请求后台线程退出(可能会在将
shutdown
放入队列之前清除队列)

请注意该模式的要点:UI对象永远不会在后台线程上访问。它们仅从UI线程进行操作。所有权有明显的分离。数据作为字节数组在线程之间传递


如果您想支持同时发生的多个请求,可以启动多个工作进程。

通常情况下,会在套接字上读取数据,这将“阻塞”(即不返回、挂断)直到一些数据被B发回。它不需要被写入来处理B的等待状态。它只是读取,而这本质上涉及到等待读取

更新以便您希望A的用户界面保持快速响应。到目前为止,最好的方法是利用用户界面库的事件队列系统。所有GUI框架都有一个中央事件循环,它将事件分派给处理程序(按钮单击、鼠标移动、计时器等)。后台线程通常有一种方法将某些内容发布到该事件队列,以便在主UI线程上执行。细节将取决于您使用的框架

例如,在Swing中,后台线程可以执行以下操作:

SwingUtilities.invokeAndWait(someRunnableObject);
final ServerReplyHandler currentHandler = ...
final Object currentReply = ...

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

        currentHandler.handleReply(currentReply);

    }
});
因此,假设您定义此接口:

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,您需要一个线程安全的请求对象队列,该队列存储
输入
对象和每个请求的处理程序。还有一个后台线程,它什么也不做,只是从队列中提取请求,将序列化的输入发送到服务器,读回回复并反序列化,然后执行以下操作:

SwingUtilities.invokeAndWait(someRunnableObject);
final ServerReplyHandler currentHandler = ...
final Object currentReply = ...

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

        currentHandler.handleReply(currentReply);

    }
});
因此,只要后台线程读回回复,它就会将其传递回主U