此类使用 AtomicBooleans。 它是线程安全的吗?

发布于 2024-07-23 04:50:15 字数 1975 浏览 4 评论 0原文

我不喜欢用 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 );
        }
    }
}
  1. 我的代码,线程是否安全,如果 2 个线程尝试同时调用 connect(),则只允许一次连接尝试。

  2. 在finally块中,我连续执行两个AtomicBoolean.set(..),会不会有问题,因为在这两个原子调用之间的间隙,一些线程可能会调用_connected.get( ) 在其他方法中?

  3. 当使用_xmppConn时,我应该执行同步(_xmppConn)吗?

更新 在方法中添加了缺少的登录调用。

I don't like to lock up my code with synchronized(this), so I'm experimenting with using AtomicBooleans. In the code snippet, XMPPConnectionIF.connect() makes a socket connection to a remote server. Note that the variable _connecting is only ever used in the connect() method; whereas _connected is used in every other methods that needs to use the _xmppConn. My questions are listed after the code snippet below.

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. Based on my code, is it thread safe to the point that if 2 threads attempt to call connect() at the same time, only one connection attempt is allowed.

  2. In the finally block, I am executing two AtomicBoolean.set(..) in succession, will be there be a problem, since during the gap between these 2 atomic calls, some threads might call _connected.get() in other methods ?

  3. When using _xmppConn, should I do a synchronized( _xmppConn ) ?

UPDATE Added missing login call into the method.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

云巢 2024-07-30 04:50:15

请记住,使用 3 个 AtomicBoolean 与使用单个锁保护这三个变量不同。 在我看来,这些变量的状态构成了对象的单一状态,因此它们应该由同一个锁来保护。 在使用原子变量的代码中,不同的线程可以使用原子变量独立更新 _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 并发实践

Keep in mind that using 3 AtomicBooleans is not the same as guarding those three variables with a single lock. It seems to me like the state of those variables constitutes a single state of the object and thus that they should be guarded by the same lock. In your code using atomic variables, it's possible for different threads to update the state of _connected, _connecting, and _shuttingDown independently -- using atomic variables will only ensure that access to the same variable is synchronized between multiple threads.

That said, I don't think synchronizing on the this is what you want to do. You only want to synchronize access to the connection state. What you could do is create an object to use as the lock for this state without getting the monitor on this. Viz:

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

If you're doing concurrent programming in Java, I would highly recommend reading Java Concurrency In Practice.

停顿的约定 2024-07-30 04:50:15
  1. 是的。 变量 _connecting 充当测试和设置锁,可防止多个并发连接尝试。

  2. 没问题 - 即使另一个线程在写入之间读取 _connected,_connecting 将阻止它尝试并发连接。

    没问题 - 即使另一个

  3. 是的,假设它的方法还不是线程安全的。

话虽如此,您的 connect() 方法以其当前的形式会让我发疯,因为它不一定连接或抛出异常。 您可以添加一个旋转循环,但这并不是一个真正合适的选择,因为除了来自多处理器计算机的最短网络跳数之外,它的生产效率会更高。 此外,低级并发原语比同步更容易出错——我强烈建议您坚持使用同步。

  1. Yes. The variable _connecting acts as a test-and-set lock that prevents multiple concurrent connection attempts.

  2. No problem -- even if another thread reads _connected between the writes, _connecting will prevent it from attempting to connect concurrently.

  3. Yes, assuming that its methods are not already thread-safe.

That being said, your connect() method would drive me nuts in its current form, since it doesn't necessarily connect or throw an exception. You could add a spin loop, but that's not really a good fit because for all but the shortest of network hops from a multiprocessor machine, it will be more efficient to yield. The low-level concurrency primitives, moreover, are a lot more error-prone than synchronized -- I strongly recommend you stick with synchronized.

回梦 2024-07-30 04:50:15

我认为其他人在他们的评论中充分涵盖了正确性。 我唯一的补充意见是,我有点担心发布在最后的位置。 看起来您可能真的想将整个块(包括 _xmppConnProvider.get() 调用)包装在 try { } finally { } 中,这样可以保证您始终释放锁。 否则,可能会发生某种未经检查的异常,并使您处于不可恢复的状态。

从风格上来说,我认为这段代码比简单地使用synchronized/Lock来实现互斥更难推理。 我将从易于推理的代码开始,如果您可以证明这是一个热点,那么只会使其变得更加复杂。

I think others are covering correctness adequately in their comments. My only additional comment would be that I am concerned a bit about the placement of the release in the finally. Seems like you might really want to wrap the whole block there (including the _xmppConnProvider.get() call) in a try { } finally { } that would guarantee you would always release the lock. Otherwise, some kind of unchecked exception could happen in there and leave you in an unrecoverable state.

Stylistically, I regard this code as much more difficult to reason about than simply using synchronized/Lock to achieve mutual exclusion. I would start with code that is easy to reason about and only make it more complicated if you can prove that this is a hot spot.

动次打次papapa 2024-07-30 04:50:15

我怀疑你的程序是线程安全的。 我不是 Java 内存模型专家,但根据我所学到的知识,操作是可以排列的,并且操作的结果可能不会按照您期望的顺序对其他线程可见。

考虑例如将 _connected 设置为 true 是否在 connect() 方法完全执行之前执行? 另一个线程可能认为您已连接,尽管您并未连接。 这只是推测 - 我不确定是否会发生特定问题。

我的观点是,你想要做的那种锁定是非常棘手的。 坚持使用同步或使用java.util.concurrent.locks包中的锁。

I doubt your program will be thread-safe. I am no Java Memory Model guru, but from what I have learned operations can be arranged and results of operations may not be visible to other threads in the order you expect.

Consider if for example setting _connected to true is executed before the connect() method is fully executed? Another thread could think that you are connected even though you aren't. This is just conjecture - I am not sure that particular problem can occur at all.

My point is rather that the sort of locking you are trying to do is extremely tricky to get right. Stick with synchronized or use the locks in the java.util.concurrent.locks package.

只有一腔孤勇 2024-07-30 04:50:15
  1. 是的,绝对满意。 因为 _connecting.compareAndSet( false, true ) 只允许一个线程进入。

  2. 你不需要设置 _connected.set( false ); 因为如果发生异常,它永远不会设置为 true。 是的,这可能不是由于继承,而是在您尚未将连接设置为 false 之前,尝试连接的其他线程不会认为连接正在进行中。

  3. 是,如果 xmppConn 不是线程安全的。

  1. Yes its definitely satisfied. as _connecting.compareAndSet( false, true ) will allow only one thread to get in.

  2. You dont need to set _connected.set( false ); as it is never set to true if exception happened. Yes its possible not due to succession but till you have not set connecting to false other Threads trying to connect wont be doing thinking that a connection is in progress.

  3. Yes if xmppConn is not Thread Safe.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文