2

Synchronized(this)でコードをロックするのは好きではないので、 AtomicBooleansを使って実験しています。コード スニペットでは、XMPPConnectionIF.connect()がリモート サーバーへのソケット接続を作成します。変数_connectingconnect()メソッドでのみ使用されることに注意してください。_connectedは、 _xmppConnを使用する必要がある他のすべてのメソッドで使用されます。私の質問は、以下のコード スニペットの後にリストされています。

private final AtomicBoolean _connecting = new AtomicBoolean( false );
private final AtomicBoolean _connected = new AtomicBoolean( false ); 
private final AtomicBoolean _shuttingDown = new AtomicBoolean( false ); 
private XMPPConnection _xmppConn;
/**
 * @throws XMPPFault if failed to connect
 */
public void connect() 
{
    // 1) you can only connect once
    if( _connected.get() )
        return;

    // 2) if we're in the middle of completing a connection, 
    //    you're out of luck
    if( _connecting.compareAndSet( false, true ) )
    {
        XMPPConnectionIF aXmppConnection = _xmppConnProvider.get();
        boolean encounteredFault = false;

        try
        {
            aXmppConnection.connect(); // may throw XMPPException
            aXmppConnection.login( "user", "password" ); // may throw XMPPException
            _connected.compareAndSet( false, true );
            _xmppConn = aXmppConnection;
        }
        catch( XMPPException xmppe )
        {
            encounteredFault = true;
            throw new XMPPFault( "failed due to", xmppe );
        }
        finally
        {
            if( encounteredFault )
            {
                _connected.set( false );
                _connecting.set( false );
            }
            else
                _connecting.compareAndSet( true, false );
        }
    }
}
  1. 私のコードに基づいて、2 つのスレッドが同時にconnect()を呼び出そうとした場合、1 つの接続試行のみが許可されるという点までスレッド セーフですか。

  2. finally ブロックで、2 つの AtomicBoolean.set(..) を連続して実行していますが、これら 2 つのアトミック呼び出しの間のギャップの間に、一部のスレッドが他のメソッドで_connected.get()を呼び出す可能性があるため、問題はありますか?

  3. _xmppConnを使用する場合、synchronized( _xmppConn )を実行する必要がありますか?

更新欠落していたログイン呼び出しをメソッドに追加しました。

4

5 に答える 5

6

3 AtomicBooleansを使用することは、単一のロックでこれら 3 つの変数を保護することと同じではないことに注意してください。これらの変数の状態は、オブジェクトの単一の状態を構成するように思われるため、同じロックで保護する必要があります。_connectedアトミック変数を使用するコードでは、異なるスレッドが、_connecting、およびの状態を個別に更新する可能性があります。アトミック変数を使用すると、同じ_shuttingDown変数へのアクセスが複数のスレッド間で同期されることが保証されるだけです。

thisとは言っても、あなたがやりたいことは同期ではないと思います。接続状態へのアクセスのみを同期したい。できることは、モニターをオンにせずに、この状態のロックとして使用するオブジェクトを作成することですthis。ビズ:

class Thing {
  Boolean connected;
  Boolean connecting;
  Boolean shuttingDown;
  Object connectionStateLock = new Object();

  void connect() {
    synchronized (connectionStateLock) {
      // do something with the connection state.
    }
  }

  void someOtherMethodThatLeavesConnectionStateAlone() {
    // free range thing-doing, without getting a lock on anything.
  }
}

Java で並行プログラミングを行っている場合は、Java Concurrency In Practiceを読むことを強くお勧めします。

于 2009-05-28T05:11:16.967 に答える
5
  1. はい。変数 _connecting は、複数の同時接続試行を防止するテスト アンド セット ロックとして機能します。

  2. 問題ありません。書き込みの間に別のスレッドが _connected を読み取ったとしても、_connecting により、同時に接続を試行することはできません。

  3. はい、そのメソッドがまだスレッドセーフではないことを前提としています。

そうは言っても、あなたの connect() メソッドは、必ずしも接続したり例外をスローしたりするとは限らないため、現在の形式では気が狂います。スピン ループを追加することもできますが、マルチプロセッサ マシンからの最短のネットワーク ホップを除いて、より効率的に生成できるため、これはあまり適していません。さらに、低レベルの同時実行プリミティブは、同期よりもはるかにエラーが発生しやすいため、同期を使用することを強くお勧めします。

于 2009-05-28T05:13:23.423 に答える
2

他の人は彼らのコメントで正しさを適切にカバーしていると思います。私の唯一の追加のコメントは、最終的にリリースが配置されることについて少し心配しているということです。ブロック全体(_xmppConnProvider.get()呼び出しを含む)をtry {} finally {}でラップして、常にロックを解除することを保証したいようです。そうしないと、そこで何らかのチェックされていない例外が発生し、回復不能な状態になる可能性があります。

様式的には、このコードは、単に同期/ロックを使用して相互排除を実現するよりも、推論するのがはるかに難しいと思います。私は推論しやすいコードから始め、これがホットスポットであることを証明できる場合にのみ、より複雑にします。

于 2009-05-28T13:56:21.503 に答える
1

あなたのプログラムがスレッドセーフになるとは思えません。私はJavaメモリモデルの第一人者ではありませんが、私が学んだことから、操作を調整することができ、操作の結果が期待した順序で他のスレッドに表示されない場合があります。

たとえば、connect()メソッドが完全に実行される前に_connectedをtrueに設定することが実行されるかどうかを検討してください。別のスレッドは、接続されていなくても接続されていると考える可能性があります。これは単なる推測です。特定の問題が発生する可能性があるかどうかはわかりません。

私のポイントは、あなたがやろうとしている種類のロックは、正しく行うのが非常に難しいということです。同期を維持するか、java.util.concurrent.locksパッケージのロックを使用します。

于 2009-05-28T06:55:19.013 に答える
0
  1. はい、間違いなく満足しています。as _connecting.compareAndSet(false、true)は、1つのスレッドのみが入ることを許可します。

  2. _connected.set(false);を設定する必要はありません。例外が発生した場合はtrueに設定されないためです。はい、それは継承によるものではありませんが、falseに接続するように設定しない限り、接続しようとしている他のスレッドは、接続が進行中であるとは考えません。

  3. xmppConnがスレッドセーフでない場合ははい。

于 2009-05-28T05:36:58.370 に答える