Частные свойства класса Java загадочным образом сбрасываются между вызовами методов - PullRequest
1 голос
/ 14 мая 2010

У меня очень странная проблема.

Свойство класса таинственным образом сбрасывается между вызовами методов.

Следующий код выполняется, поэтому вызывается конструктор, а затем вызывается метод parseConfiguration.Наконец, вызывается processData.

Метод parseConfiguration устанавливает для свойства "recursive" значение "true".Однако, как только он входит в «processData», «recursive» становится «false».

Эта проблема не изолирована ни для одного класса - у меня есть несколько примеров этого в моем коде.

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

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

Я на JDK 1.6.0_19 на Linux.

Пожалуйста, помогите!

/**
 * This class or its superclasses are NOT threaded and don't extend Thread
 */
public class DirectoryAcquirer extends Manipulator
{
/**
 * @var Whether to recursively scan directories
 */
private boolean recursive = false;

/**
 * Constructor
 */
public DirectoryAcquirer()
{
}

/**
 * Constructor that initialises the configuration
 *
 * @param config
 * @throws InvalidConfigurationException
 */
public DirectoryAcquirer(HierarchicalConfiguration config) throws InvalidConfigurationException
{
    super(config);
}

@Override
protected void parseConfiguration() throws InvalidConfigurationException
{
    // set whether to recurse into directories or not
    if (this.config.containsKey("recursive"))
    {
    // this.recursive gets set to "true" here
        this.recursive = this.config.getBoolean("recursive");

    }
}

@Override
public EntityCollection processData(EntityCollection data)
{
// here this.recursive is "false"
    this.logger.debug("processData: Entered method");
}
}

Ответы [ 6 ]

0 голосов
/ 18 мая 2010

Спасибо за ваши ответы.

Похоже, проблема была связана с кастингом:

/**
 * 
 */
public class Manipulator
{
    // both constructors call parseConfiguration()

    protected void parseConfiguration() throws InvalidConfigurationException
    {
      // don't do anything. this is a default method that can optionally be 
      // overridden by subclasses
    }
}

В некоторых местах я ссылался на объект как DirectoryAcquirer, а в другое время он передавался на манипулятор. Я думаю, что это мешало, поэтому пустой метод parseConfiguration был вызван после того, как был вызван дочерний метод - или что-то в этом роде.

Во всяком случае, я сделал метод parseConfiguration в аннотации суперкласса, и теперь все нормально.

0 голосов
/ 15 мая 2010

Я бы попробовал запустить этот кусок кода с помощью отладчика и посмотреть, что именно происходит.Потому что есть несколько простых сценариев ошибки:

  • методы не выполняют то, что вы ожидаете
  • ваш this.recursive = true код не достигнут вообще
  • вы вообще не устанавливаете значение true
  • вы не проверяете правильное свойство
  • вы не проверяете свойство правильно

Отладчик поможет вам стот.Пожалуйста, обновляйте информацию, когда вы ее видите, и у вас может возникнуть идея помочь себе.

0 голосов
/ 15 мая 2010

Мне действительно пахнет так, как разные вызовы методов для одного объекта вызываются из двух разных потоков . Это полностью объясняет «таинственный сброс» -подобного поведения.

В двух словах, изменения переменных-членов не гарантированно будут видны между потоками. Подробнее о Java Memory Model читайте в подробностях.

Чтобы преодолеть это, есть пара вариантов. Два самых простых (для вашего примера) будут либо a) с использованием модификатора volatile:

private volatile boolean recursive = false;  

или b) использование java.util.concurrent.AtomicBoolean

private AtomicBoolean recursive = new AtomicBoolean(false);

Синхронизированное ключевое слово, хотя и является важным инструментом для многопоточного программирования, не является тем, что можно правильно использовать, не понимая, как оно работает (и не работает) более подробно. Попробуйте сначала приведенные выше предложения.

0 голосов
/ 14 мая 2010

Я бы выбрал подход старой школы.

Добавить это в parseConfiguration:

System.err.println("parseConfiguration: this = " + this + ", config = " + config
    + ", recursive = " + recursive + " on " + Thread.currentThread());

Добавьте это к processData:

System.err.println("processData: this = " + this + ", config = " + config
    + ", recursive = " + recursive + " on " + Thread.currentThread());

Он сообщит вам, в каком порядке они вызываются, и проверит, что они ссылаются на одни и те же объекты и делают это в одних и тех же потоках.

Единственные причины, о которых я могу думать, это либо многопоточность (parseConfiguration и processData вызываются в разных потоках), либо они вызываются в разных объектах.

0 голосов
/ 14 мая 2010

Нет достаточного контекста, чтобы объяснить, что происходит, но я могу гарантировать, что частные атрибуты не сбрасываются «спонтанно» в Java. Если бы я угадал возможные причины, я бы проголосовал за одно или несколько из следующих:

  • методы не вызываются в том порядке, в каком вы думаете,
  • один или один из методов перегружен или переопределен, или
  • вы смотрите на разные объекты.

Например, если суперкласс вызывает метод parseConfiguration, то может случиться так, что фактический используемый метод parseConfiguration - один из подкласса DirectoryAcquirer.

Объявление методов как synchronized вряд ли поможет. В лучшем случае это предотвратит одновременное обращение этих двух потоков одним и тем же объектом. Он не будет полностью останавливать вызовы из другого потока.

0 голосов
/ 14 мая 2010

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

Линия

// this.recursive gets set to "true" here
    this.recursive = this.config.getBoolean("recursive");

смущает меня. Откуда вы знаете, что this.config.getBoolean("recursive") возвращает истину? Он получает только логическое значение a , хранящееся с рекурсивным ключом. Это значение может быть истинным, но также может быть и ложным.

Если вы убедитесь, что this.config.getBoolean("recursive") действительно возвращает true, тогда вы должны смотреть за пределы своего кода. Что-то на самом деле вызывало protected void parseConfiguration()? Если нет, то this.recursive никогда не будет установлен за пределы своего первоначального объявления.

Допустим, что protected void parseConfiguration() был вызван, и он правильно установил this.recursive в true. Затем вам нужно поискать остальную часть класса (только этот класс, потому что this.recursive является закрытым, и посмотрите, может ли какой-либо другой код сбросить значение.

Другие возможности состоят в том, что вы не имеете дело с одним и тем же объектом между двумя вызовами protected void parseConfiguration(). Иногда это происходит, когда вы используете карты объектов или другие формы кэширования объектов, которые предоставляют возможность переключать объекты, если они используются или написаны неправильно. Возможно, вы захотите включить некоторые записи, которые будут выводить идентификатор объекта (toString()) вместе со значением this.recursive.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...