RMI会自动"thread safe"吗?



我正在学习如何使用Java RMI来代码分发应用程序。我编写了一个简单的程序只是为了测试某些内容,我只是通过客户端将整数传达给服务器,而服务器将它们累积在静态变量中。这是代码:

服务器:

public class Adder extends UnicastRemoteObject implements IAdder {
/**
 * 
 */
private static final long serialVersionUID = 8229278619948724254L;
private static Integer sum = 0; 
static Logger logger = Logger.getLogger("global");
protected Adder() throws RemoteException {
    super();
    // TODO Auto-generated constructor stub
}
@Override
public void add(int i) throws RemoteException {
    System.out.println("Got: " + i);
    synchronized (sum) {
        sum += i;
        System.out.println("The new sum is: " + sum);
    }
}
@Override
public int result() throws RemoteException {
    System.out.println("The sum is: " + sum);
    return sum;
}

public static void main(String[] args) {
    System.setSecurityManager(new SecurityManager());
    try  {
        logger.info("Building remote object");
        Adder obj = new Adder();
        logger.info("Binding");
        Naming.rebind("SommaServer", obj);
        logger.info("Ready");
    } catch (Exception e) {
        e.printStackTrace();
    }
}
}

客户端:

public class Client extends Thread {
static Logger logger = Logger.getLogger("global");
private IAdder obj;
private int array[] = new int[3];
public static void main(String[] args) {
    try {
        Client c1 = new Client(1);
        Client c2 = new Client(2);
        c1.start();
        c2.start();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public void work(int i) throws RemoteException {
    obj.add(i);
}
public void run()  {
    Random rn = new Random();
    try {
        work(array[0]);
        work(array[1]);
        work(array[2]);
    } catch (Exception e) {
        e.printStackTrace();
    }   
}
public Client(int i) throws Exception {
    if (i == 1) {
        array[0] = 1;
        array[1] = 3;
        array[2] = 4;
    }
    if (i == 2) {
        array[0] = 7;
        array[1] = 2;
        array[2] = 5;
    }
    logger.info("Remote object lookup");
    obj = (IAdder) Naming.lookup("rmi://localhost/SommaServer");
}
}

在客户端的主体中,我创建了两个客户端线程,然后运行它们(我知道没有同步检查,但我只是在尝试一下)。每个线程都有一个数字来馈送到服务器。服务器接收它们,然后将它们添加为全部togheter。

所以,由于我正在处理线程,所以我的第一个想法是我需要在总和的更新上使用锁定,否则由于线程的交织,我可能会出现错误。因此,服务器中的同步块。

,但是,要查看会发生什么,我删除了块,我仍然得到正确的结果,一直以来(值为22)。

只是为了确保我也制作了客户端的"本地"版本,该版本更新了本地变量:

public class Client extends Thread {
private static Integer sum = 0;
static Logger logger = Logger.getLogger("global");
private IAdder obj;
private int array[] = new int[3];
public static void main(String[] args) {
    try {
        Client c1 = new Client(1);
        Client c2 = new Client(2);
        c1.start();
        c2.start();
        c1.join();
        c2.join();
        System.out.println("Ricevuto: " + sum);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public void work(int i) throws RemoteException {
    //obj.add(i);
    synchronized(sum) {
        sum += i;
    }
}
public void run()  {
    Random rn = new Random();
    try {
        work(array[0]);
        work(array[1]);
        work(array[2]);
    } catch (Exception e) {
        e.printStackTrace();
    }   
}
public Client(int i) throws Exception {
    if (i == 1) {
        array[0] = 1;
        array[1] = 3;
        array[2] = 4;
    }
    if (i == 2) {
        array[0] = 7;
        array[1] = 2;
        array[2] = 5;
    }
}
}

随着同步,我得到了正确的结果,没有得到各种数字(8、15、14、22 ...)

那么,到底是怎么回事?我怀疑RMI是否可以这样安全。

额外的问题:当我在RMI中绑定一个对象时,我到底是什么约束?我称为naming.rebind()或类的对象的特定实例(然后当我查找它时,我只会得到一个新实例?)?

您的同步被打破了,因此您不能通过实验推导太多。每次进行sum += i时,都会为sum分配一个新的整数。因此,线程在两个不同的整数上同步。

另外,您正在绑定Sommatore的一个实例,但是您正在显示加法器的代码。

但是要回答您的问题,您正在绑定一个实例,而RMI不会神奇地创建该实例的副本。它也不会同步此实例的所有调用。因此,您需要确保代码是线程安全的。仅仅因为没有适当的同步并不意味着它总是这样,但快速测试会产生正确的结果。您可以用眼睛闭上10次越过道路,永不死。这并不意味着它是安全的。

RMI会自动"线程安全"?

no。

额外的问题:当我在RMI中绑定一个对象时,我到底是什么约束?我称为naming.rebind()或类的对象的特定实例(然后当我查找它时,我只会得到一个新实例?)?

实际上都不是。您正在绑定 stub ,它与您提供给bind()方法的远程对象实例相关。

最新更新