在Java中,当obj可以为null时,使用synchronized(obj)代码块。

huangapple go评论81阅读模式
英文:

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

问题

  • 我有一个工作正常的Java synchronized( obj ) { ... } 代码块。
  • 我想扩展它的行为,这样无论何时调用 synchronized( obj == null ),整个代码块都会被跳过。
  • 目前我得到空指针异常。
  • 有没有办法在 synchronized( ... ) { ... } 代码块中实现这一点?
英文:
  • 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?

答案1

得分: 2

在同步块中,直接通过同步块本身来直接实现这一点是没有简单方法的。

你可以在进入同步块之前进行一些if判断的魔法操作,或者将整个逻辑委托给一个专门的类或方法。无论哪种情况,实际的锁监视器绝不能为null

例如,如果实际监视器为null,你可以将这个逻辑移动到下面并将该类封装。

public class NullableMonitor {
    private Object monitor;

    // 构造函数

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

    // 不要忘记同步
    public synchronized void setMonitor(Object monitor) {
        this.monitor = monitor;
    }
}

然后在你的同步块中使用这个作为监视器。

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

然而,重要的是NullableMonitor中的setter方法要在自身上进行同步,否则在进入和if检查之间,有人可能会对monitor进行操作。

然而,这样做的后果是你不能从同步块内部调用它。


需要注意的是,你的整体概念似乎存在缺陷,看起来非常危险。人们之所以说同步的监视器/锁绝不能为null且应为final,是有原因的。监视器不应被更改,否则很容易搞乱逻辑,遇到不同的问题。

英文:

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.

答案2

得分: 1

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

我认为最简单的方法是定义一个私有方法来处理这个逻辑此外可以用函数式接口替代要执行的任务并将行为作为参数传递以供多次使用

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) {
        // 业务代码
    }
}

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

如果您不依赖于专用的锁对象您还可以使用

synchronized(this){..} // this指的是该类的实例
synchronized(MyClass.class){..} // 用于静态情况
英文:
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&lt;R,T&gt; {
    public abstract R execute(List&lt;T&gt; args) throws Exception;
}

Wrapper&lt;String, String&gt; wrapper = new Wrapper() {
    @Override
    public String execute(List&lt;String&gt; input) {
        // business code
    }
}

private &lt;R,T&gt; R doStuff(Object lock, Wrapper&lt;R,T&gt; 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

答案3

得分: 0

    var freezeObj = obj; // 假设obj是volatile的
    if (freezeObj != null) {
      synchronized (freezeObj) {
        // ...
      }
    }
英文:

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

答案4

得分: 0

以下是翻译好的内容:

最好使用 Java 中的 Lock 类,而不是 Object 类。

public void doStuff(Lock lock){
    lock.lock();
    // 进行操作
    lock.unlock();
}
英文:

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

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

huangapple
  • 本文由 发表于 2020年7月24日 15:06:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/63068524.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定