我不想用 synchronized(this)来锁定我的代码,所以我正在尝试使用 AtomicBooleans 。在代码段中, XMPPConnectionIF.connect()与远程服务器建立套接字连接。请注意,变量 _connecting 仅在 connect()方法中使用;而 _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 );
}
}
}
根据我的代码,如果2个线程同时尝试调用 connect(),那么线程是否安全,只允许一次连接尝试。
在finally块中,我连续执行两个AtomicBoolean.set(..),会出现问题,因为在这两个原子调用之间的间隙中,某些线程可能会调用 _connected .get()在其他方法中?
使用 _xmppConn 时,我应该执行 synchronized(_xmppConn)吗?
更新在方法中添加了缺少的登录调用。
答案 0 :(得分:6)
请注意,使用3 AtomicBoolean
s 不与使用单个锁保护这三个变量相同。在我看来,这些变量的状态构成了对象的单个状态,因此它们应该被同一个锁保护。在使用原子变量的代码中,不同的线程可以独立地更新_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。
答案 1 :(得分:5)
是。变量_connecting充当测试和设置锁,可防止多个并发连接尝试。
没问题 - 即使另一个线程在写入之间读取_connected,_connecting也会阻止它同时尝试连接。
是的,假设它的方法不是线程安全的。
话虽这么说,你的connect()方法会以当前的形式让我疯狂,因为它不一定连接或抛出异常。你可以添加一个旋转循环,但这并不是一个很好的选择,因为除了多处理器机器中最短的网络跳跃之外,它的产生效率会更高。此外,低级并发原语比同步更容易出错 - 我 强烈 建议您坚持使用synchronized。
答案 2 :(得分:2)
我认为其他人在评论中充分涵盖了正确性。我唯一的另一个评论是,我关注的是在最终版本中放置版本。看起来你可能真的想在try {} finally {}中包装整个块(包括_xmppConnProvider.get()调用),这将保证你总是释放锁。否则,某种未经检查的异常可能会在那里发生并使您处于不可恢复的状态。
从风格上讲,我认为这个代码比单纯使用synchronized / Lock实现互斥更难以推理。我会从易于推理的代码开始,只有当你能证明这是一个热点时才会使它变得更复杂。
答案 3 :(得分:1)
我怀疑你的程序是否是线程安全的。我不是Java内存模型大师,但是根据我所学到的操作,可以安排操作,并且操作结果可能不会按照您期望的顺序显示给其他线程。
考虑是否在完全执行connect()方法之前执行设置 _connected 为true?即使你不是,另一个线程可能会认为你是连接的。这只是猜想 - 我不确定特定问题是否会发生。
我的观点是,你想要做的那种锁定是非常棘手的。坚持使用synchronized或使用 java.util.concurrent.locks 包中的锁。
答案 4 :(得分:0)
是的,它绝对满意。 as _connecting.compareAndSet(false,true)将只允许一个线程进入。
您不需要设置_connected.set(false);因为如果发生异常,它永远不会设置为true。是的,它可能不是由于继承,但直到你没有设置连接到false其他尝试连接的线程不会认为连接正在进行中。
是的,如果xmppConn不是线程安全的话。