【问题标题】:How to tell if in a synchronised block in Java?如何判断是否在 Java 的同步块中?
【发布时间】:2020-08-23 10:37:28
【问题描述】:

这一定有一个简单的答案,但我只是不知道它是什么......如果我说,在 Java 中执行以下操作:

class First{
    public void first(){
        Second second=new Second();

        synchronized(this){
            second.second(this);
        }

        second.second(this);
    }
}

如何在Second.second 中检查在调用此方法之前已获得同步锁,如果不是这种情况可能会抛出异常?例如:

class Second{
    public void second(First first){
        if(!/*want to test that lock obtained for first, but don't know how*/){
            throw new RuntimeException("Must lock first!");
        }
    }
}

如果上面的代码不明显,我希望第二次调用 Second.second 以抛出 RuntimeException

【问题讨论】:

    标签: java synchronized


    【解决方案1】:

    有一个方法:)

    public void second(First first) {
        if (!Thread.holdsLock(first)) throw new IllegalStateException("Lock required");
    }
    

    但是,你不想要这个。

    你想要的是这个:

    public void second(First first) {
        synchronized (first) {
            // do stuff
        }
    }
    

    如果一个线程持有一个锁,然后你再次同步,那是免费的,不会破坏任何东西:它不需要时间,也不会冻结你的线程。您可以重新获取您的线程已经持有的锁。 Java 维护一个计数器。 (锁是可重入的)。

    要求调用者获得这个锁似乎很愚蠢;为什么不自己购买呢?如果调用者已经获得它,没问题。没有时间浪费,代码继续运行。

    NB:就代码风格而言,抛出 RuntimeException 是不好的,并且在消息中放置一个感叹号是非常糟糕的(想想看;90% 以上的异常消息会以 ! 结尾查看日志真的很烦人)。我认为你也不需要像这样提前退出的大括号。因此,如果您必须使用“检查并抛出”样式,则 sn-p 会为您编写一些修复程序:)

    【讨论】:

    • 我可能只是为了表述我的示例而粗心地简短,并且不会真正编写这样的代码。
    【解决方案2】:

    我希望第二次调用 Second.second 来抛出 RuntimeException 如果这对上面的代码不明显。

    我认为这是个坏主意。要么您希望 First 处理锁,其次不关心,要么 Second 处理锁,无论它是 First 还是 Third。

    如果我们将它与标准库中的类进行比较,我们可能会看看 HashMap 与 ConcurrentMap。 HashMap 是一个非线程安全的类 - 也就是说,它与您的示例中的 Second 相同。 ConcurrentMap 是一个“线程安全”类——也就是说,它处理自己的同步操作。

    这实际上取决于什么构成“线程安全”,因此需要更多地了解如何使用该类来了解线程安全的 ConcurrentMap 方法是否真正提供线程安全。

    除了 First 之外,还有其他人可以访问 Second 的同一个实例,并且您正在从这个角度防范多线程访问吗?如果是这样,ConcurrentMap 方法可能更合适。在多线程环境中,Second 本身是否会发生多个操作?如果是这样,手动锁定 First 会更合适。

    使用 map 对 Second 进行多项操作的示例。

    Map<Integer, String> map...
    ... // lets say map has 3 elements by this point and there are 2 threads running.
    if (map.size() < 4)
    { // <--- thread may switch here, so both threads are inside the if block
        map.put(map.size(), "This map is too small");
      // Both threads have put in "This map is too small" to the map.
    }
    

    对于这个简单的sn-p,无论map是HashMap还是ConcurrentMap,我们都无法防止“This map is too small”被添加两次。因此,尽管 ConcurrentMap 提供了“线程安全”,但这段代码实际上并不是线程安全的。因此,我们需要一个外部锁:

    ...
    synchronized (map)
    {
       if (map.size() < 4)
       {
          map.add(map.size(), "This map is too small");
       }
    }
    

    所以在这种情况下,ConcurrentMap 将没有任何好处,使用更简单的 HashMap 将是正确的选择。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-15
      • 1970-01-01
      • 1970-01-01
      • 2010-10-28
      • 2012-04-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多