RMI框架解析二
远程方法中的参数与返回值的传递:
当客户端调用服务器端的远程对象的方法时,客户端会向服务器端传递传递参数,服务器端则会根据客户端传递的返回值。
RMI规范对参数以及返回值的传递作了以下规定:
1). 只哟基本类型的数据、远程对象以及可序列化的对象才可以作为参数或返回值进行传递。
如果参数或返回值是一个远程对象,那么把它的存根对象传递到接收方,也就是说,接收方得到的是远程对象的存根对象。
如果参数或返回值是可序列化的对象,那么直接传递该对象序列化数据。也就是说,接收方,接收方得到的是发送方的可序列化的对象的复制品。
如果参数或返回值是基本类型的数据,那么直接传递该数据的序列化的数据,也就是说,接收方得到是发送方的基本类型的复制品。
回调客户端的远程对象
远程对象不仅可以位于服务器端,也可以位于客户端。
只要服务器端获得了客户端的远程对象的存根对象,服务器端也能调用客户端的远程对象的方法,这种调用过程称为回调。
在股票报价系统中,服务器端不断地把最新的股票价格发送到客户端,并且在客户端的界面上显示出来。客户端有一个StockQuote远程对象,它的quote(String stockSymbol, double price)方法在客户端的界面上打印参数指定的股票的价格。服务器端有一个StockQuoteRegistry远程对象,它能调用客户端的StockQuote远程对象的quote()方法。
在SimpleClient类的main()方法先通过注册表获得StockQuoteRegistryImpl远程对象的存根对象,因此RMI框架会把这个远程对象的存根对象发送到服务器端,使得服务器端的StockQuoteRegistryImpl远程对象的缓存中保存了这个存根对象。
package com.unmi.reback;
import java.rmi.*;
public interface StockQuoteRegistry extends Remote{
public void registerClient(StockQuote client)throws RemoteException;
public void unregisterClient(StockQuote client)throws RemoteException;
}
package com.unmi.reback;
import java.rmi.*;
import java.rmi.server.*;
import java.util.*;
public class StockQuoteRegistryImpl extends UnicastRemoteObject
implements StockQuoteRegistry, Runnable{
private static final long serialVersionUID = 1L;
protected HashSet<StockQuote> clients;
public StockQuoteRegistryImpl()throws RemoteException{
clients = new HashSet<StockQuote>();
}
public void run(){
//创建一些股票代号
String[] symbols = new String[] {"SUNW", "MSFT", "DAL", "WUTK", "SAMY", "KATY"};
Random rand = new Random();
double values[] = new double[symbols.length];
//为每个股票分配任意价格
for(int i=0; i < values.length; i++){
values[i] = 25.0 + rand.nextInt(100);
}
for (;;){
//随机取出一个股票
int sym = rand.nextInt(symbols.length);
// 修改股票的价格
int change = 100 - rand.nextInt(201);
values[sym] = values[sym] + ((double) change) / 100.0;
if (values[sym] < 0) values[sym] = 0.01;
Iterator<StockQuote> iter = clients.iterator();
while (iter.hasNext()){
StockQuote client = iter.next();
try{
client.quote(symbols[sym], values[sym]);
}catch (Exception exc){
System.out.println("删除一个无效的客户");
iter.remove();
}
}
try { Thread.sleep(1000); } catch (Exception ignore) {}
}
}
public void registerClient(StockQuote client)throws RemoteException{
System.out.println("加入一个客户");
clients.add(client);
}
public void unregisterClient(StockQuote client)throws RemoteException{
System.out.println("删除一个客户");
clients.remove(client);
}
}
package com.unmi.reback;
import java.rmi.registry.LocateRegistry;
import javax.naming.*
public class SimpleServer{
public static void main( String args[] ){
try{
LocateRegistry.createRegistry(1099);
StockQuoteRegistryImpl registry=new StockQuoteRegistryImpl();
Context namingContext=new InitialContext();
namingContext.rebind( "rmi:StockQuoteRegistry", registry);
System.out.println( "服务器注册了一个StockQuoteRegistry对象" );
new Thread(registry).start();
}catch(Exception e){
e.printStackTrace();
}
}
}
package com.unmi.reback;
import java.rmi.*;
/** 客户端的远程对象接口 */
public interface StockQuote extends Remote{
public void quote(String stockSymbol, double price)throws RemoteException;
}
package com.unmi.reback;
import java.rmi.*;
import java.rmi.server.*;
public class StockQuoteImpl extends UnicastRemoteObject
implements StockQuote{
public StockQuoteImpl()throws RemoteException{}
public void quote(String symbol, double value)throws RemoteException{
System.out.println(symbol+": "+value);
}
}
package com.unmi.reback;
import javax.naming.*;
public class SimpleClient{
public static void main( String args[] ){
String url="rmi://localhost/";
try{
Context namingContext=new InitialContext();
StockQuoteRegistry registry=(StockQuoteRegistry)namingContext.lookup(url+"StockQuoteRegistry");
StockQuote client=new StockQuoteImpl();
registry.registerClient(client);
}catch( Exception e){
e.printStackTrace();
}
}
}
远程对象的并发访问:
RMI框架允许远程对象被客户端并发访问,远程对象在本身的实现中必须保证是线程安全的,即当远程对象被多个客户端同时访问时,不会出现对共享资源的竞争。
如下:在两个客户进程中,都有若干生产者线程和消费者线程,它们都访问服务器端的同一个堆栈Stack远程对象。生产者线程远程调用Stack对象的push()方法,向堆栈中放入产品,消费者线程使用Stack对象的pop()方法从堆栈中取出产品。
客户进程生产者
|
服务器进程 |
Stack |
客户进程消费者 |
package com.unmi.syn;
import java.io.Serializable;
public class Apple implements Serializable{
private static final long serialVersionUID = 1L;
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
package com.unmi.syn;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Pool extends Remote {
public Apple get() throws RemoteException;
public void put(Apple apple) throws RemoteException;
}
package com.unmi.syn;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
public class PoolImpl extends UnicastRemoteObject implements Pool {
private static final long serialVersionUID = 1L;
private List<Apple> pools;
public PoolImpl() throws RemoteException {
pools = new ArrayList<Apple>();
}
public synchronized Apple get() throws RemoteException {
while (pools.isEmpty()) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Apple apple = pools.remove(0);
this.notify();
return apple;
}
public synchronized void put(Apple apple) throws RemoteException {
while (pools.size() >= 5) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
pools.add(apple);
this.notify();
}
}
package com.unmi.syn;
import java.rmi.registry.LocateRegistry;
import javax.naming.InitialContext;
public class Server {
public static void main(String[] args) {
try {
LocateRegistry.createRegistry(1099);
InitialContext ctx = new InitialContext();
ctx.bind("rmi:pool", new PoolImpl());
System.out.println("server ready:");
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.unmi.syn;
import java.rmi.RemoteException;
public class Consumer implements Runnable {
private Pool pool;
public Consumer(Pool pool) {
this.pool = pool;
}
public void run() {
try {
Apple apple = pool.get();
System.out.println("consume: " + apple.getId());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
package com.unmi.syn;
import java.rmi.RemoteException;
public class Producer implements Runnable {
private Pool pool;
private int j = 0;
public Producer(Pool pool) {
this.pool = pool;
}
public void run() {
try {
Apple apple = new Apple();
apple.setId(j++);
pool.put(apple);
System.out.println("produce: " + apple.getId());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
package com.unmi.syn;
import javax.naming.InitialContext;
public class Client {
public static void main(String[] args) throws Exception {
InitialContext namingContext = new InitialContext();
Pool pool = (Pool)namingContext.lookup("rmi://localhost/pool");
Producer p = new Producer(pool);
for (int i=0; i<5; i++) {
new Thread(p).start();
}
Consumer c = new Consumer(pool);
for (int i=0; i<5; i++) {
new Thread(c).start();
}
}
}