Java RMI - 我不明白为什么没有共享状态



我对Java RMI有一个问题(可能是一个基本的理解问题)。我有一个"服务器"对象和人的"客户端"。两者都是RMI对象(它们实现到不同的远程接口)。客户端在服务器上自行注册,并存储在列表中。服务器稍后将向客户端分发一些工作并获取结果。

问题是,存储客户端的列表似乎不是共享的(我没有更好的描述)。也就是说,如果一个客户端调用服务器接口并注册自己,它将被添加到列表中,但调用似乎只是本地的。

以下是代码的最小示例:

IRemote.java

import java.rmi.Remote;
import java.rmi.RemoteException;
public interface IRemote extends Remote {
    public void remoteCall() throws RemoteException;
}

RemoteImple.java

import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.ArrayList;
public class RemoteImpl implements IRemote, Serializable {
    public ArrayList<Integer> list = new ArrayList<Integer>();
    @Override
    public void remoteCall() throws RemoteException {
        list.add(23);
        System.out.println("In remote call: "+list.size());
    }
}

RegAndServer.java

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RegAndServer {
    public static void main(String[] args) throws RemoteException, InterruptedException {
        Registry reg = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
        RemoteImpl impl = new RemoteImpl();
        reg.rebind("server", impl);
        while(true) {
            Thread.sleep(2000);
            System.out.println("Server sees: "+impl.list.size());
        }
    }
}

Client.java

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class Client {
    public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
        IRemote remote = (IRemote) Naming.lookup("//localhost/server");
        remote.remoteCall();
    }
}

如果我启动服务器java RegAndServer,它将在循环中输出"server sees:0"。如果我现在在另一个shell中启动客户端java Client,我会得到"in remote call:1"。但是,服务器进程仍然输出"服务器看到:0"。

为什么会这样?我做错了什么?事实上,我以为我已经理解了Java RMI:(

您的RemoteImpl不应该是Serializable(您不想通过网络发送它的副本,只想发送存根,运行时会为您这样做),并且必须扩展UnicastRemoteObject

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
public class RemoteImpl extends UnicastRemoteObject implements IRemote {
    public ArrayList<Integer> list = new ArrayList<Integer>();
    public RemoteImpl() throws RemoteException {
    }
    @Override
    public void remoteCall() throws RemoteException {
        list.add(23);
        System.out.println("In remote call: "+list.size());
    }
}

请记住,代码是在服务器端执行的,即使调用是从客户端进行的,因此消息"in remote call"将显示在服务器日志中。

尝试更改:

public ArrayList<Integer> list = new ArrayList<Integer>();

public static ArrayList<Integer> list = new ArrayList<Integer>();

如果有帮助的话。确保安全:

public class RemoteImpl implements IRemote, Serializable {
    private static volatile ArrayList<Integer> list = new ArrayList<Integer>();
    @Override
    public void remoteCall() throws RemoteException {
        synchronized(list) {
            list.add(23);
        }
        System.out.println("In remote call: "+list.size());
    }
}

最新更新