Как синхронизировать доступ по статическому полю суперкласса? - PullRequest
3 голосов
/ 17 января 2012

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

public class A {

    private static MyAPI instance = null;

    protected synchronized static MyAPI getAPI() throws Exception {
        if (instance == null){
            // init API;
        }
        return instance;
    }

    // other methods

}

И у меня есть несколько классов, которые наследуются от класса A и должны выполнять действия с API. Я работаю в многопоточной среде, и API может работать по одному за раз, поэтому я должен убедиться, что все подклассы не работают в API одновременно. Для этого я синхронизирую суперкласс при доступе к API в подклассах:

public class B extends A {

    public void myMethod(){
        synchronized (A.class) {
            myAPI = getAPI();
            // do stuffs with myAPI
        }
    }
}

В этом решении я блокирую весь класс, а не только экземпляр API, поэтому другие методы моего класса A недоступны, когда подкласс работает с API и производительность может быть уменьшена.

Как вы думаете, это лучшее решение или вы знаете лучший способ?

Спасибо.

Ответы [ 4 ]

2 голосов
/ 17 января 2012

Здесь я хотел бы рассмотреть две проблемы:

  1. Во-первых, поскольку объект MyAPI действует как одноэлементный, тот факт, что другие классы наследуются от класса Aне имеет значения.С тем же успехом можно просто указать, что другие классы в неиерархической структуре ссылаются на синглтон.

  2. Во-вторых, синхронизация должна выполняться внутри кода MyAPI, и таким образом вы можете контролироватьгранулярность синхронизации любым способом, который вы хотите.Это позволяет вам также добиться лучшей инкапсуляции, и вам не нужно беспокоиться о плохом поведении вызывающего абонента, который забывает получить блокировку, прежде чем продолжить.Это может быть метод, функциональность и т. Д.

Например:

class MyAPI {

  public synchronized void doWork1() { // class level lock
     ...
  }

  public void doWork2 {
    synchronized (someLockObject) {
      ...
    }
  }

  public void doWork3 { // related to doWork2, lock the same object
    synchronized (someLockObject) {
      ...
    }
  }
1 голос
/ 17 января 2012

Если вы не хотите блокировать весь класс, вы можете заблокировать статический объект, который вы используете только в этом методе:

public class A {

    private static MyAPI instance = null;
    protected static Object lockForMyMethod = new Object(); //have a static lock

    // other methods    
}

public class B extends A {

    public void myMethod(){
        synchronized (A.lockForMyMethod) { //do not lock on A.class
            myAPI = getAPI();
            // do stuffs with myAPI
        }
    }
}
1 голос
/ 17 января 2012

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

0 голосов
/ 17 января 2012

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

В зависимости от вашей среды, рассмотрите возможность использования ExecutorService .

Например: вы можете использовать ThreadPoolExecutor с фиксированным размером пула потоков, равным 1, и передавать свои задания этому исполнителю.

Таким образом, вы можете убедиться, что ваш API используется только в методе call() Callable, который вы отправили. Поскольку у вас работает только один поток, вам не нужно беспокоиться о параллельном доступе к API.

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

...