java - RMI:多个客户端访问服务器时正确同步

标签 java rmi

几天前我开始使用 Java RMI。我想知道以下示例是否正确同步。

考虑以下向客户端提供资源字符串的服务器类。它永远不会两次提供相同的资源,因此它将提供的字符串存储在列表中。这是 ServerEngine 类:

package dummy;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.LinkedList;

public class ServerEngine implements Server {
    private final String s1 = "Resource Object 1";
    private final String s2 = "Resource Object 2";
    private final LinkedList<String> list = new LinkedList<>();
    private final int timer = 5000;

    public static void main(String[] args) {
        try {
            String name = "server";
            ServerEngine engine = new ServerEngine();
            Server stub = (Server) UnicastRemoteObject.exportObject(engine, 0);
            Registry registry = LocateRegistry.getRegistry();
            registry.rebind(name, stub);
            System.out.println("ServerEngine bound");
        } catch (Exception e) {
            System.err.println("ServerEngine exception:");
        }
    }

    @Override
    public String getResource() throws RemoteException {
        Object lock = new Object();

        if ( ! list.contains(s1)) {
            synchronized (lock) {
                // wait to ensure concurrency
                try {
                    lock.wait(timer);
                } catch (InterruptedException ex) {}
            }
            list.add(s1);
            return s1;
        }

        if ( ! list.contains(s2)) {
            list.add(s2);
            return s2;
        }

        return null;
    }
}

服务器接口(interface):

package dummy;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Server extends Remote {
    public String getResource(boolean synced) throws RemoteException;
}

和客户:

package dummy;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Client {
public static void main(String[] args) {
        try {
            String name = "server";
            Registry registry = LocateRegistry.getRegistry();
            Server server = (Server) registry.lookup(name);

            boolean sync = args.length > 0;
            String s = server.getResource(sync);
            System.out.println("Resource: " + s);
        } catch (Exception e) {
            System.err.println("Client exception:");
        }
}

}

ServerEngine 的实现方式会导致并发问题。如果在五秒内从两个不同的虚拟机启动两个客户端,那么它们都将获得相同的返回字符串。

根据我迄今为止的研究,这是我解决问题的方法:

package dummy;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.LinkedList;

public class ServerEngine implements Server {
    private final String s1 = "Resource Object 1";
    private final String s2 = "Resource Object 2";
    private final LinkedList<String> list = new LinkedList<>();
    private final int timer = 5000;

    public static void main(String[] args) {
        try {
            String name = "server";
            ServerEngine engine = new ServerEngine();
            Server stub = (Server) UnicastRemoteObject.exportObject(engine, 0);
            Registry registry = LocateRegistry.getRegistry();
            registry.rebind(name, stub);
            System.out.println("ServerEngine bound");
        } catch (Exception e) {
            System.err.println("ServerEngine exception:");
        }
    }

    private synchronized String localGetResource() {
        Object lock = new Object();

        if ( ! list.contains(s1)) {
            synchronized (lock) {
                // wait to ensure concurrency
                try {
                    lock.wait(timer);
                } catch (InterruptedException ex) {}
            }
            list.add(s1);
            return s1;
        }

        if ( ! list.contains(s2)) {
            list.add(s2);
            return s2;
        }

        return null;

    }

    @Override
    public String getResource() throws RemoteException {
        return localGetResource();
    }
}

我想知道这是否是一个可行的解决方案。有什么注意事项吗?我真的需要第二个函数还是可以直接同步 getResource() ?

最佳答案

您的同步在多个级别上被破坏:

  • 除非您希望其他线程notify()您,否则您不应该wait()某些事情。
  • 您只实现了 double-checked locking 的一半,翻译为“无锁定”,因为相同的值可能会多次出现在列表中。
  • 您应该查看 java.util.concurrent 下正确的线程安全集合实现,而不是手动执行此操作。

关于java - RMI:多个客户端访问服务器时正确同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47231982/

相关文章:

带逗号的 Java 资源包格式

java - 为什么我不能从链表中删除重复项?

java - 我们如何从firebase下载android studio中的文件(pdf),使其不会出现在手机的内部存储中?

java - 调试 RMI 连接

java - 安卓编程: Authentication and data exchange with Java EE

java - 让等待线程跳过剩余的等待/继续

java - 使用 ColorConverterOp Java 将 RGB JPEG 转换为 CMYK JPEG

java - 如何使用回调来实现 session 模式?

java - 如何使用 rmi 将记录插入 java 中的 mysql(jdbc)?

java - NoSuchObjectException - 表中没有这样的对象