简体   繁体   中英

How to synchronize access on a static field of a super class?

I have a class which contains a static field that acts like a singleton :

public class A {

    private static MyAPI instance = null;

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

    // other methods

}

And I have multiple classes which inherit from the class A and needs to perform actions on the API. I work in a multi-threaded environment and the API can work once at a time, so I have to ensure that all the subclasses don't work on the API at the same time. To do that, I synchronize the super class when I access the API in subclasses :

public class B extends A {

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

With this solution, I lock the entire class instead of just the API instance, so the other methods of my class A are not available when a subclass work on the API and performances can be decreased.

Do you think this is the best solution or do you know a better way ?

Thanks.

There are two issues that I'd consider here:

  1. First, because the MyAPI object acts as a singleton, the fact that other classes inherit from class A is irrelevant. You might as well just have other classes in a non-hierarchical structure refer to the singleton.

  2. Secondly, the synchronization should be done inside the code of MyAPI, and this way you can control the synchronization granularity any way that you want. This lets you also achieve better encapsulation, and you don't need to worry about a bad-behaving caller who forgets to acquire a lock before proceeding. It can be per method, per functionality, etc.

For example:

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) {
      ...
    }
  }

不确定为什么需要锁定对静态成员的每次访问,但考虑使用AtomicReference和它的getAndSet()方法以获得更好的性能。

If you don't want to lock on the entire class, you may lock on a static object that you use only in that method:

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
        }
    }
}

I work in a multi-threaded environment and the API can work once at a time, so I have to ensure that all the subclasses don't work on the API at the same time.

Depending on your environment, consider to use the ExecutorService .

For example: you could use a ThreadPoolExecutor with a fixed thread-pool size of 1 and submit your jobs to that executor.

That way you can ensure your API is only used within the call() method of the Callable you submitted. Since you have only one thread working, you don't have to worry about concurrent access of the API.

Again, i don't know the environment you are working so maybe it is a bad idea or simple not possible to solve the problem with a ExecutorService.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM