Защита статической переменной класса - PullRequest
2 голосов
/ 02 марта 2011

У меня довольно тривиальный вопрос о статической переменной. Я строю решение, которое свободно следует пути или RMI. На моем сервере у меня есть класс ComputeEngine, который будет выполнять «Задачи» (экземпляры классов с методом «выполнить»). Однако ComputeEngine будет содержать глобальную переменную, к которой должны будут обращаться различные задачи, каждая из которых выполняется в своем собственном потоке. Какой лучший способ дать доступ к этому? Я хочу, чтобы все было как можно слабее связано. Общая глобальная статическая переменная в моем классе ComputeEngine будет списком. Должен ли я получить геттер для этой статической переменной? У меня будет блокировка чтения / записи в моем классе ComputeEngine, чтобы дать доступ к моему глобальному списку. Это тоже будет статичным и должно быть распространено. Я ищу рекомендации по предоставлению доступа к глобальной статической переменной в классе.

Ответы [ 7 ]

4 голосов
/ 02 марта 2011

Если вы хотите отделить его, лучший способ - передать объект обратного вызова при создании Task.

interface FooListManipulator {
  void addFoo( Foo f );
  List<Foo> getFooList();
}

class Task {
  private FooListManipulator fooListManipulator;

  public Task( FooListManipulator fooListManipulator ) {
    this.fooListManipulator = fooListManipulator;
  }
}

. Таким образом, сама задача не должна предполагать, ктосоздал его и как хранится список.

А в вашем ComputeEngine вы будете делать что-то вроде этого:

class ComputeEngine {

  private static List<Foo> fooList;

  class Manipulator implements FooListManipulator {
    public void addFoo( Foo f ) {
      synchronized( fooList ) {
        fooList.add( f );
      }
    } 

    public List<Foo> getFooList() {
      return Collections.unmodifiableList( fooList );
    }
  }

  private Task createTask() {
    return new Task( new Manipulator() );  
  }
}

Если вы хотите изменить хранилище на fooList позже (что вы должны учитывать, так как статические глобальные переменные не очень хорошая идея), Task останется неизменным.Кроме того, вы сможете провести юнит-тест Task с помощью имитатора манипулятора.

2 голосов
/ 03 марта 2011

Nooooooooooooooooooooooooooooooooooooooooooooooooooo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

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

В случае RMI по умолчанию вы загружаете ненадежный код оттуда, куда направляет клиент (верхний совет, при использовании RMI, используйте -Djava.rmi.server.useCodebaseOnly=true). Как глобальная статика, этот код может зависеть от состояния вашего сервера (при условии наличия доступного загрузчика классов и т. Д.).

2 голосов
/ 02 марта 2011

Я ищу лучший способ предоставления доступа к глобальной статической переменной

По лучшим методам у вас не должно быть таких переменных.

Должен ли я получить геттер для этой статической переменной?У меня будет блокировка чтения / записи в моем классе ComputeEngine, чтобы дать доступ к моему глобальному списку.

Нет, вы не должны предоставлять такой метод получения.Просто addTask(Task task) или execute(task) метод.Метод синхронизации будет работоспособным решением.

1 голос
/ 02 марта 2011

Кажется, все утверждают, что вы используете List для хранения очереди заданий, но я не вижу этого в вашем вопросе.Но если это так, или если манипуляции со списком не зависят друг от друга - то есть, если вы просто добавляете или удаляете из списка, а не сканируете список и удаляете некоторые элементы из середины как частьзадание - тогда вам следует рассмотреть возможность использования BlockingQueue или QueueDeque вместо List и простого использования пакета java.util.concurrent.Эти типы не требуют внешнего управления блокировками.

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

class AggregateStatistics {
    private static final AggregateStatistics aggregateStatistics = 
                   new AggregateStatistics();

    public static AggregateStatistics getAggregateStatistics () {
           return aggregateStatistics;
    }

    private List list = new ArrayList ();
    private Lock lock = new ReentrantLock();

    public void updateAggregates (...) {
        lock.lock();
        try {
            /* Mutation of the list */
        }
        finally {
            lock.unlock();
        }
    }
}

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

Никогда не передает коллекцию, которая в параллельную средутолько вызовет у вас проблемы.Вы всегда можете обойти неизменную «обертку», хотя, если она действительно подходит, используя java.util.Collections.unmodifiableList(List) и аналогичные методы.

1 голос
/ 02 марта 2011

После ответа @biziclop, но с другим разделением)

Вы можете разделить свой код на следующие части.

interface Task {
    void execute();
}

public final class TaskExecutor{
     TaskExecutor(List<Task> tasks){}
     void addTask(Task task){synchronized(tasks){tasks.add(task);}}
}

Чем,

public class SomeTaskAdder {
     SomeTaskAdder(TaskExecutor executor){}
     void foo(){
           executor.addTask(new GoodTask(bla-bla));
     }
}

public class SomeTasksUser {
     SomeTasksUser(List<Task> tasks){synchronized(tasks){bla-bla}}
}

чем, вы должны создать свои объекты с помощью какой-нибудь магической инъекции конструктора)

1 голос
/ 02 марта 2011

У меня есть несколько рекомендаций для вас:

  1. Ваша «Задача» звучит как Runnable, замените «execute» на «run», и вы получите много вещей бесплатно.Как и все классные классы в java.util.concurrent.
  2. Сделайте ComputeEngine одним синглтоном с помощью техники, описанной в этом посте .Чтобы было понятно, используйте подход «enum» от Джоша Блоха (2-й ответ на этот вопрос).
  3. Сделайте свой список членом ComputeEngine
  4. Задачи используют ComputeEngine.saveResult (...), который изменяет список.
  5. Рассмотрите возможность использования java.util.concurrent.Executors для управления вашим пулом задач.
1 голос
/ 02 марта 2011
  • не возвращайте свой список из получателя, так как вы не будете знать, что люди будут с ним делать (они могут что-то добавить и сломать вашу блокировку). Так что сделайте это:

    статический синхронизированный список getTheList () { вернуть новый ArrayList (theList); }

и применять геттер только в том случае, если это кому-то действительно нужно

  • не реализовывать никаких сеттеров; вместо этого реализуйте addItemToList () и removeItemToList ()

кроме этого, глобальная статическая переменная не одобряется ...

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