我正在学习如何使用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()或类的对象的特定实例(然后当我查找它时,我只得到一个新实例?)?
答案 0 :(得分:4)
您的同步已中断,因此您无法通过实验推断出同步。每次执行sum += i
时,都会将新的整数分配给sum
。所以线程在两个不同的整数上同步。
此外,您正在绑定Sommatore的实例,但您正在显示Adder的代码。
但是要回答你的问题,你要绑定一个实例,RMI不会神奇地创建该实例的副本。它不会同步所有对此实例的调用。因此,您需要确保代码是线程安全的。仅仅因为快速测试产生了正确的结果,尽管没有适当的同步并不意味着它总是那样。你可以闭着眼睛过马路10次,永不死。这并不意味着它是安全的。
答案 1 :(得分:1)
RMI是否自动“线程安全”?
没有
额外的问题:当我在RMI中绑定一个对象时,我到底绑定了什么?我调用Naming.rebind()或类的对象的特定实例(然后当我查找它时,我只得到一个新实例?)?
实际上两者都没有。您正在绑定存根,它与您提供给bind()
方法的远程对象实例无关联。