简体   繁体   中英

synchronized( obj ) block in java when obj can be null

  • I have a java synchronized( obj ) {... } block which works fine.
  • I want to extend its behavior so that whenever synchronized( obj == null ) is called, the entire block is skipped.
  • Currently I get a null pointer exception.
  • Is there any way to achieve this with synchronized(... ) {... } block?

There is no simple way to achieve this directly through the sync block.

You either do some if magic before the block or you delegate this whole thing to a dedcated class or method. In all cases, the actual lock monitor must never be null .

An example, instead of having the actual monitor being null , you move this logic one below and wrap the class.

public class NullableMonitor {
    private Object monitor;

    // constructor

    public boolean isPresent() {
        return monitor != null;
    }

    // dont forget the synchronized
    public synchronized void setMonitor(Object monitor) {
        this.monitor = monitor;
    }
}

And then in your sync block you use this as monitor.

final NullableMonitor monitor = new NullableMonitor(myObject);
...
synchronized (monitor) {
    if (monitor.isPresent()) {
        return;
    }
    ...
}

It is critical though that the setter in NullableMonitor is synchronized on itself, otherwise someone can mess with the monitor between entering and the if-check.

This has as consequence though that you can not call it from within your sync block.


Note that your overall concept seems to be flawed and appears very dangerous. There is a reason why people are saying that a monitor/lock for a sync must never be null and should always be final . The monitor should not be changed, you can easily mess up your logic and run into different issues by that.

private void doStuff(Object lock) {
    if (lock == null) return;
    synchronized(lock) {
        // doStuff
    }
}

I think the easiest way is to define a private method handling this logic. Furthermore one could replace the task to execute with a functional interface and pass the behavoir as a parameter for multiple usage.

interface Wrapper<R,T> {
    public abstract R execute(List<T> args) throws Exception;
}

Wrapper<String, String> wrapper = new Wrapper() {
    @Override
    public String execute(List<String> input) {
        // business code
    }
}

private <R,T> R doStuff(Object lock, Wrapper<R,T> w) throws Exception {
    if (lock == null) return null;
    synchronized(lock) {
        return w.execute();
    }
}

If you do not rely on a dedicated lock object, you can also use:

synchronized(this){..} // this refers to instance of this class
synchronized(MyClass.class){..} // for static usecase

The tricky thing is that the object reference may change between the null check and the synchronization. So I would suggest the following:

    var freezeObj = obj; // assuming obj is volatile
    if (freezeObj != null) {
      synchronized (freezeObj) {
        // ...
      }
    }

It would be better to use Lock class from java instead of Object class..

  public void doStuff(Lock lock){
      lock.lock();
      //doStuff
      lock.unlock();
  }

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