Этот класс использует AtomicBooleans. Это потокобезопасно? - PullRequest
2 голосов
/ 28 мая 2009

Я не люблю блокировать свой код с синхронизированным (это) , поэтому я экспериментирую с использованием 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) ?

ОБНОВЛЕНИЕ В метод добавлен отсутствующий вызов входа в систему.

Ответы [ 5 ]

6 голосов
/ 28 мая 2009

Имейте в виду, что использование 3 AtomicBoolean s не то же самое, что защита этих трех переменных с помощью одной блокировки. Мне кажется, что состояние этих переменных составляет единое состояние объекта и, следовательно, они должны охраняться одной и той же блокировкой. В вашем коде, использующем атомарные переменные, разные потоки могут независимо обновлять состояние _connected, _connecting и _shuttingDown - использование атомарных переменных гарантирует только то, что доступ к той же самой переменной синхронизируется между несколькими потоками.

Тем не менее, я не думаю, что синхронизация на this - это то, что вы хотите сделать. Вы только хотите синхронизировать доступ к состоянию соединения. То, что вы могли бы сделать, - это создать объект, который будет использоваться в качестве блокировки для этого состояния без включения монитора 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.
  }
}

Если вы занимаетесь параллельным программированием на Java, я настоятельно рекомендую прочитать Java Concurrency In Practice .

5 голосов
/ 28 мая 2009
  1. Да. Переменная _connecting действует как блокировка проверки и установки, которая предотвращает множественные одновременные попытки подключения.

  2. Нет проблем - даже если другой поток читает _connected между записями, _connecting предотвратит попытку одновременного подключения.

  3. Да, при условии, что его методы не являются поточно-ориентированными.

При этом ваш метод connect () привел бы меня в бешенство в его текущей форме, поскольку он не обязательно соединяется или выдает исключение. Вы можете добавить спин-петлю, но это не совсем подходит, потому что для всех, кроме самых коротких сетевых скачков с многопроцессорной машины, это будет более эффективно. Более того, низкоуровневые примитивы параллелизма гораздо более подвержены ошибкам, чем синхронизированные - я сильно рекомендую придерживаться синхронизированного.

2 голосов
/ 28 мая 2009

Я думаю, что другие адекватно освещают свои комментарии. Единственным дополнительным комментарием было бы то, что я немного обеспокоен размещением релиза в версии finally. Похоже, вы действительно хотите обернуть весь блок там (включая вызов _xmppConnProvider.get ()) в попытку {} finally {}, которая гарантирует, что вы всегда снимите блокировку. В противном случае, там может произойти какое-то неконтролируемое исключение, которое может привести к неисправимому состоянию.

Стилистически, я считаю, что этот код гораздо сложнее рассуждать, чем просто использовать synchronized / Lock для достижения взаимного исключения. Я бы начал с кода, который легко рассуждать, и усложнит его, только если вы сможете доказать, что это горячая точка.

1 голос
/ 28 мая 2009

Я сомневаюсь, что ваша программа будет поточно-ориентированной. Я не гуру модели памяти Java, но из того, что я узнал, могут быть организованы операции, и результаты операций могут быть невидимы для других потоков в том порядке, в котором вы ожидаете.

Подумайте, выполняется ли, например, установка _connected в true до того, как метод connect () будет полностью выполнен? Другой поток может подумать, что вы связаны, хотя вы не связаны. Это всего лишь предположение - я не уверен, что какая-то конкретная проблема может возникнуть вообще.

Моя точка зрения скорее заключается в том, что блокировка, которую вы пытаетесь сделать, чрезвычайно сложна, чтобы получить право. Придерживайтесь синхронизированных или используйте блокировки в пакете java.util.concurrent.locks .

0 голосов
/ 28 мая 2009
  1. Да, это определенно удовлетворено. поскольку _connecting.compareAndSet (false, true) позволит войти только одному потоку.

  2. Вам не нужно устанавливать _connected.set (false); так как оно никогда не устанавливается в true, если произошло исключение. Да, это возможно не из-за преемственности, но до тех пор, пока вы не установите соединение с ложным, другие потоки, пытающиеся соединиться, не будут думать, что соединение установлено.

  3. Да, если xmppConn не безопасен для потоков.

...