このクラスは AtomicBoolean を使用します。スレッドは安全ですか?
-
06-09-2019 - |
質問
コードをロックするのは好きではありません 同期済み(これ), 、それで私は使って実験しています アトミックブール値. 。コードスニペットでは、 XMPPConnectionIF.connect() リモートサーバーへのソケット接続を確立します。変数に注意してください。 _接続中 でのみ使用されます 接続する() 方法;一方 _接続済み を使用する必要がある他のすべてのメソッドで使用されます。 _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 );
}
}
}
私のコードに基づくと、2つのスレッドが呼び出しを試みた場合にスレッドセーフですか? 接続する() 同時に、接続試行は 1 回だけ許可されます。
Finally ブロックでは、2 つの AtomicBoolean.set(..) を連続して実行しています。これら 2 つのアトミック呼び出しの間のギャップ中に、一部のスレッドが呼び出しを行う可能性があるため、問題は発生しますか? _connected.get() 他の方法では?
使用するとき _xmppConn, 、やるべきですか? 同期化( _xmppConn ) ?
アップデート 欠落していたログイン呼び出しをメソッドに追加しました。
解決
はい。変数 _connecting は、複数の同時接続試行を防止するテストおよび設定ロックとして機能します。
問題ありません。書き込みの間に別のスレッドが _connected を読み取った場合でも、_connecting によって同時に接続を試行できなくなります。
はい、そのメソッドがまだスレッドセーフではないことを前提としています。
そうは言っても、あなたの connect() メソッドは、必ずしも接続したり例外をスローしたりするわけではないので、現在の形式では私を怒らせるでしょう。スピン ループを追加することもできますが、マルチプロセッサ マシンからの最短のネットワーク ホップを除くすべてのネットワーク ホップについては、その方が効率的に収益を得ることができるため、実際には適切ではありません。さらに、低レベルの同時実行プリミティブは、同期型プリミティブよりもはるかにエラーが発生しやすくなります。 強く 同期を使用することをお勧めします。
他のヒント
3 AtomicBoolean
sを使用すると、単一のロックで、これら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並行処理での読書をお勧めしますの練習。
私は他の人が彼らのコメントに十分に正確さをカバーしていると思います。私の唯一の追加のコメントは私が最終的に発売の配置について少し心配ということでしょう。あなたが本当に試しに(_xmppConnProvider.get()呼び出しを含む)は、ブロック全体をラップする場合がありますように思える{}最後に{}あなたは常にロックを解除します保証すること。それ以外の場合は、チェックされない例外のいくつかの種類がある中で起こると回復不能な状態であなたを残すことができます。
文体、私ははるかに困難についての理由を単に相互排除を達成するために、同期/ロックを使用するよりも、このコードを考えています。私はについて推論とだけあなたは、これがホットスポットであることを証明することができれば、それはより複雑にするのは簡単なコードで始まります。
私はあなたのプログラムは、スレッドセーフではないだろう。私はJavaのメモリモデルの第一人者午前ませんが、私が学んだことから事業を配置することができ、操作の結果は、あなたが期待するために、他のスレッドに表示されない場合があります。
connect()メソッドが完全に実行される前にtrueにの_connectedのの設定が実行される場合の例ならば、考えてみましょうか?別のスレッドが、あなたがいない場合であっても接続されていることを考えることができます。これは単なる推測である - 私は特定の問題は全く発生する可能性があることを確認していない
。私のポイントは、あなたがやろうとしているロックの並べ替えが権利を取得することは非常にトリッキーであるということではなくです。同期に固執かの java.util.concurrent.locks のパッケージにロックを使用します。
はい、間違いなく満足です。_connecting.compareAndSet( false, true ) では、1 つのスレッドのみがアクセスを許可されます。
_connected.set( false ) を設定する必要はありません。例外が発生した場合は決して true に設定されないためです。はい、継承によるものではありませんが、接続を false に設定しない限り、接続しようとしている他のスレッドは接続が進行中であると考えて実行されません。
xmppConn がスレッド セーフでない場合ははい。