Как сохранить параметр объекта неизменным в классе Runnable в Java? - PullRequest
5 голосов
/ 20 сентября 2011

У меня есть класс Runnable, например:

Class R1 implements Runnable {
  private static final Log LOGGER = LogFactory.getLog(R1.class);
  private final ObjectClass obj;
  private final SomeService service;

  public R1(ObjectClass obj, SomeService service) {
     this.obj = obj;
     this.service = service;
  }

  @override
  public void run() {
    String value = this.obj.getSomeValue();
    LOGGER.debug("Value is " + value);
    // some actions, such as:
    // service.someMethod(obj);
  }
}

Я использую объект ExecutorService для выполнения R1 и помещения R1 в очередь.Но позже за пределами R1 я изменяю значение в ObjectClass, которое я передал в R1, чтобы действия в R1 после getSomeValue () не работали так, как я ожидал.Если я хочу сохранить значение объекта ObjectClass в R1 без изменений, что я могу сделать?Предположим, что объект большой и имеет много методов get и set.

Чтобы прояснить проблему, мне нужно передать объект obj в объект класса обслуживания, который также используется в качестве параметра в классе runnable.Я изменил оригинальные коды соответственно.

Ответы [ 7 ]

1 голос
/ 20 сентября 2011

В зависимости от характера вашей программы, есть несколько вариантов.

Вы можете «Добросовестно переопределить клонирование» (пункт 11 в Effective Java ) и клонировать объект перед его передачей.в управляемый.Если переопределение клона не работает для вас, лучше выполнить одно из следующих действий:

  1. Создать новый экземпляр объекта вручную и скопировать значения из obj.
  2. Добавить подмножество данных, содержащихся в obj.Таким образом, вместо передачи obj в конструктор, вы должны передать someValue. Я бы рекомендовал этот метод, чтобы вы только снабжали R1 необходимыми данными, а не всем объектом.

В качестве альтернативы, еслине имеет значения, что данные в obj изменяются до выполнения R1, тогда вам только нужно убедиться, что obj не изменит , пока R1 выполняется. В этом случае вы можете добавить ключевое слово synchronize к методу getSomeValue() и затем R1 синхронизировать obj следующим образом:

@Override
public void run() {
  synchronize (obj) {
    String value = obj.getSomeValue();
  }
  // some actions.
}
1 голос
/ 20 сентября 2011

Согласно комментариям, очевидно, у моего предложенного решения есть проблемы.

Таким образом, следуйте другим советам по созданию нового экземпляра и копированию требуемых свойств. Или создайте легкий объект данных, который содержит нужные вам свойства. В любом случае, я считаю, что вам нужно 2 экземпляра, чтобы делать то, что вы хотите.


Я предлагаю вам реализовать метод клонирования, который создает новый экземпляр.

http://download.oracle.com/javase/1,5,0/docs/api/java/lang/Cloneable.html

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

В вашем классе R1,

public R1(ObjectClass obj) {
   //this.obj = obj;
   this.obj = obj.clone();
}

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

0 голосов
/ 20 сентября 2011

Проблема в том, что в модели памяти Java потоки могут (и делают) кэшировать значения полей. Это означает, что если один поток обновляет поле (объекта ObjectClass), другие потоки не будут "видеть" изменения - они все равно будут смотреть на свое кэшированное (устаревшее) значение.

Чтобы сделать изменения видимыми в потоках, у вас есть две опции:

  1. Создайте поля, которые вы будете изменять в ObjectClass volatile - ключевое слово volatile заставляет потоки не кэшировать значение поля (т. Е. Всегда использовать последнее значение)
  2. синхронизирует доступ, как к чтению, так и к записи, к полям - все изменения, сделанные в синхронизированном блоке, видны другим потокам, синхронизирующимся на одном и том же объекте блокировки (если вы синхронизируете методы, объект this используется в качестве блокировки)
0 голосов
/ 20 сентября 2011

Предположение: вам все равно, работает ли R1 со старыми данными.

Затем вы можете изменить свой код на:

public class R1 implements Runnable {
  private final String value;

  // Option 1: Pull out the String in the constructor.
  public R1(ObjectClass obj) {
    this.value = obj.getSomeValue(); // Now it is immutable
  }

  // Option 2: Pass the String directly into the constructor.
  public R1(String value) {
    this.value = value; // This constructor has no coupling
  }

  @Override public void run() {
    // Do stuff with value
  }
}

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

0 голосов
/ 20 сентября 2011

Если возможно, попробуйте сделать ваш ObjectClass неизменным. (изменения состояния не поддерживаются). В Java вы должны «сделать это сами»; нет понятия «константный» объект (как в C ++)

Возможно, вы можете иметь свой Object Object Class, но создать новый класс ImmutableObjectClass, который берет ваш источник в ctor.

0 голосов
/ 20 сентября 2011

если objet слишком большой ,

может быть неизменный ParameterObject, с достаточным количеством данных / метода , лучше.

0 голосов
/ 20 сентября 2011

Передайте объект конструктору и не сохраняйте ссылку на него.

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