2016-12-08 8 views
0

分散アプリケーションをコード化するためにJava RMIを使用する方法を学習しています。 私はいくつかのことをテストするための単純なプログラムを書いています。クライアントを介して整数をサーバに渡すだけで、サーバはそれらを静的変数に蓄積します。ここでは、コードは次のようになります。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(); 
    } 
} 

} 

クライアント:私は2つのクライアントスレッドを作成し、それらを実行し、クライアントのメインで

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です)。

ちょうど私がローカル変数を更新しすぎて、クライアントの「ローカル」バージョン、作ら確かに:私は、様々な数(8を取得せずに、私は、正しい結果を得る同期して

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; 
    } 
} 

} 

を、15 、14、22 ...)

どういうことが起こっているのですか?私はRMIがスレッドセーフであることに疑いがあります。

その他の質問:RMIでオブジェクトをバインドすると、どのようにバインドされているのですか? Naming.rebind()を呼び出すオブジェクトの特定のインスタンス、またはクラス(そして、それを見てみると、ただ新しいインスタンスが得られますか?)

答えて

4

同期が壊れているため、実験ではあまり推測できません。 sum += iを実行するたびに、新しい整数をsumに割り当てます。したがって、スレッドは2つの異なる整数で同期します。

また、Sommatoreのインスタンスをバインドしていますが、Adderのコードが表示されています。

あなたの質問に答えるには、インスタンスをバインドしています.RMIはそのインスタンスのコピーを魔法のように作成しません。このインスタンスへのすべての呼び出しを同期化しません。したがって、コードがスレッドセーフであることを確認する必要があります。適切な同期がないにもかかわらず、クイックテストで正しい結果が得られたとしても、必ずしもそうなるわけではありません。あなたは目を10回閉めて死ぬことはありません。それが安全だということではありません。

+0

私はそれを逃した! – Paul

+0

私はそれが私の主張であり、同期なしで(私は修正しました、ありがとう)、それは正しく動作しませんか?サーバーへの呼び出しが順不同であっても、最終結果は正しいと思いますが、書き込みが同期化されているかのように、それは私を捨ててしまいます。 – Paul

+2

再び、欠陥のあるロジック。シンプルなテストではうまく動作しませんが、同期は「スレッドセーフです」という意味ではありません。単に失敗させる方法を見つけていないことを意味します。多くの異なるスレッドから数百万回ではなく数百万回を呼び出し、いくつかのプロセスで実行していると、おそらく失敗が見られるでしょう。例はスレッドセーフではないことを証明するのに十分です。スレッドセーフであることを証明するために、例は十分ではありません。目を閉じて道路を横切って生きているのと同じように、目を閉じて道路を横切ることが安全であることを証明していません。 –

1

RMIは自動的に「スレッドセーフ」ですか?

エクストラ質問:私は、RMIでオブジェクトをバインドするとき、私は正確に何を結合していますか? Naming.rebind()を呼び出すオブジェクトの特定のインスタンス、またはクラス(そして、それを見てみると、ただ新しいインスタンスが得られますか?)

実際にはどちらでもありません。 スタブをバインドしています。これは、bind()メソッドに指定したリモートオブジェクトインスタンスに取り返しがつきません。

関連する問題