Java 重新进入同步块的成本

Java 重新进入同步块的成本,java,synchronization,Java,Synchronization,问题:当监视器已锁定时,重新输入同步块的成本是多少 例如: Object lock; void outer() { synchronized (lock) { innerOne(); innerTwo(); } } void innerOne() { synchronized (lock) { /* ... */ } } void innerTwo() { synchronized (lock) { /* ... */ } } 上面的意

问题:当监视器已锁定时,重新输入同步块的成本是多少

例如:

Object lock;
void outer()
{
    synchronized (lock)
    {
        innerOne();
        innerTwo();
    }
}

void innerOne() { synchronized (lock) { /* ... */ } }
void innerTwo() { synchronized (lock) { /* ... */ } }
上面的意图是当线程在
lock
上同步时,始终调用
innerOne
innerTwo

如果存在不可忽略的成本,是否有任何方法可以调用以放入
assert
语句中?我能找到的最接近的方法是调用
lock.notify()
,并捕获
IllegalMonitorStateException
,例如

boolean isMonitorHeld(final Object object)
{
    try { object.notify(); return true }
    catch (final IllegalMonitorStateException e) { return false; }
}
其使用方式如下:

void innerOne() { assert isMonitorHeld(lock); /* ... */ }
对这两种选择的风格或其他选择有何评论

编辑

我希望得到更全面的答案,而不仅仅是“等着瞧吧”。我无法预见代码可能遇到的所有潜在情况,甚至无法创建一个测试来展示这些情况。我想了解同步机制是如何工作的,以了解它在不同情况下的性能。我知道在不同的平台上可能会采用不同的同步方式。在哪种情况下会有所不同(主要是在Solaris和Linux操作系统上)


直观地说,我不认为重新进入同步块会有明显的成本,因为我能找到的大多数文章都暗示无争用锁是便宜的。但是,在这些方法中添加同步块是不对的,因为它给人的印象是,不需要先在锁上进行同步,就可以调用同步块。断言给出了一个更好的意图,但它看起来像一个相当丑陋的黑客。我想知道没有更合理的替代方案是否有充分的理由。

衡量一下。在我的系统中,可能没有什么不同。在你身上可能有。您部署到的系统可能更为不同。

因此,您的问题是问一件事,而问题的主体是问其他事情。我可以肯定地说,测试线程是否持有锁的算法将非常慢。比不检查慢得多。抛出这样的异常代价很高。关于例外的第一条规则不要将异常用于程序中的常规流控制。异常是控制流结构的一种形式,但您应该仅将其用于错误流控制。主要是因为无论何时抛出异常,它都必须收集堆栈跟踪以将其放入异常中。那是一项昂贵的手术。您应该明确地使用异常,但仅用于它们的设计目的:错误流控制

如果要测试currentThread是否持有锁,应使用Thread.holdsLock(对象监视器)

)


至于重新进入同步块的成本是多少,currentThread已经是一个持有者。我没有一个非常详细的答案。我怀疑打电话给holdsLock要花多少钱。这可能是第一次检查,如果返回false,那么请求将此线程添加到等待此对象监视器的线程列表中的成本会更高。如果这是真的,它会直接跳进街区。Java线程,如果它想提高性能,对锁所有者来说,这个问题的答案最好是非常快的。对于不拥有锁的线程来说,它的成本肯定更高。如果确切的算法是什么,我打赌它在《驯服线程》一书或Java规范中有答案。

为什么不测量它?如果你不能测量差异,那么就没有。谢谢,我完全错过了线程方法。关键是我在两个或多个方法之间有一些共享的逻辑。初始方法提供同步,然后执行共享逻辑。根据我的经验,断言对于描述(在代码中)开发人员的意图很有用。在我的例子中,实际上在监视器上再次同步会给人一种错误的印象,因为这意味着可以在不首先获得锁的情况下调用该方法。在没有先获得锁的情况下,永远不应该调用该方法,因此如果它这样做了,那就是编程错误。我重新阅读了你的问题,我认为它更清楚一些。你只想知道你是否可以在这两个方法中使用assert语句,而不是像在你的第一段代码中那样再次获得锁。如果方法是公共的,我不会这么做,但是对于私有方法,使用断言将强制调用方锁定,因此如果有人重构了您的代码,如果他们忘记获取锁,它将捕获。我打赌性能方面我打赌它与使用同步块相同。