2016-03-23 9 views
2

私は、デバイスに入力を求めて応答を受け取り、すべてをアトミック操作として受け取るメソッドを作成しようとしています。ここでなぜ同期が機能しないのですか?

は私のコードは、(queryメソッドがに焦点を当てすべきか、本当にある)である:

public class DeviceConnection implements Runnable{ 
    //For query 
    static int test = 0; 

    //For writeline 
    static PrintWriter out = null; //(initialized in constructor) 

    //Only important for readline 
    static String[] systemMessage=new String[10]; 
    static int messageIn=0; 
    static int messageOut=0; 
    static boolean connected = false; 
    static boolean endConnect = true; 

    static PrintStream logWriter; //(initialized in constructor) 
    static String serverName="";//(initialized in constructor) 
    static int socketNum;//(initialized in constructor) 

    /** REALLY ONLY NEED TO LOOK AT THIS METHOD 
     * Atomic operation to ask for data from device 
     * and get a response. 
     * @param line - query to be passed to device 
     * @return response from device 
     */ 
    public synchronized String query(String line){ 
     int temp = test; 
     System.err.print("foo" + test); 
     System.err.print(this); 
     String response; 
     writeLine(line); 
     response = readLine(); 
     System.err.println("bar" + test + " should be " + temp); 
     test = temp+1; 
     return response; 
    } 


/** 
    * Writes a query to the device. 
    *<p> 
    * Does <b>not</b> get or handle any response 
    * @param line - query to be passed to device 
    */ 
    public synchronized void writeLine(String line) { 
     out.println(line + "\n"); 
    } 

/** 
    * Reads a response from device. 
    *<p> 
    * Should never be used outside of <code>query</code> 
    * as this could create a race condition if another 
    * thread is writing to the device at the same time. 
    * @return 
    */ 
    private synchronized String readLine() { 

     String response; 
     long start, stop; 

     if (messageIn != messageOut) { // new message exists 
      response = systemMessage[messageOut]; 
      messageOut = (messageOut + 1) % 10; 
     } else { 
      start = System.currentTimeMillis(); 
      try { 
       if (connected) { // if connected wait for heatbeats 
        //wait(15000); 
        wait(); 
        start = System.currentTimeMillis(); 
       } else { // if not connected ignore heartbeats 
        wait(); 
        start = System.currentTimeMillis(); 
       } 
      } catch (InterruptedException e) { return "Interrupted"; } 
      stop = System.currentTimeMillis(); 

      if (stop - start < 12000) { // heart beats running at 5 s 
       if (messageIn != messageOut) { // new message exists 
        response = systemMessage[messageOut]; 
        messageOut = (messageOut + 1) % 10; 
       } else { 
        return null; 
       } 
      } else { // heart beats lost 
       response = "Heart beats lost"; 
       logWriter.println(response); 
       if (connected) { // connection lost on client side 
        logWriter.println("Connection to " + serverName + 
             " lost on client side"); 
        sleep(60000); 
        connect(serverName,socketNum); 
       } 
      } 
     } 
     return response; 
    } 
} 

通常query方法は細かい動作しますが、時々私はそうのような出力が得られます。

[email protected](other System.out stuff printed here) 
[email protected] should be 59 
bar60 should be 59 

これはどのように可能ですか?メソッドはオブジェクト上でロックされていませんか?オブジェクトは明らかにプリントと同じですが、何とか2つのメソッドが同時に実行されています。

+0

どのように多くのインスタンスが? –

+0

実際にはシングルトンクラスなので、1つだけです。しかし、これは問題ではありません。出力が両方のメソッドが同じオブジェクトから呼び出されていることがはっきりと分かります(ID: 'DeviceConnection @ 7bd0bf6d') – River

答えて

4

queryから呼び出されるreadLineメソッドはwaitを呼び出してロックを解除し、別のスレッドがqueryを同時に呼び出すことができるようにします。

条件変数を使用してループ内で常にwaitを呼び出す必要があります。ifを使用して待機するかどうかを決めるパターンには欠陥があります。スレッドがロックを再度取得すると、現在の状態を確認する必要があります。

現在のスレッドがこのオブジェクトのモニターを所有する必要があります。waitリリースロックがObject#waitのドキュメントで説明されていることを

。スレッド は、このモニタの所有権を解放し、別のスレッド が、このオブジェクトのモニタで待機しているスレッドに、通知メソッドまたはnotifyAllメソッドを呼び出すことによって のいずれかを起動するまで通知するまで待機します。その後、 スレッドは、モニタの所有権を再取得できるまで待機し、 が実行を再開します。 1つの引数のバージョンと同様に

、割り込みやスプリアスウェイクアップは が可能であり、このメソッドは常にループで使用する必要があります:あなたが持っているDeviceConnectionクラスの

synchronized (obj) { 
    while (<condition does not hold>) 
     obj.wait(); 
    ... // Perform action appropriate to condition 
} 
+0

私はそれが今働いていると思います。他のスレッドが 'query'メソッドを実行し始めることを許可せずに、私のスレッドが条件を待つことができるように、既に' synchronized'メソッドの中で別のオブジェクトを同期/待機させたいと思います。 – River

関連する問題