重新进入同步块的成本

时间:2012-02-10 09:46:20

标签: java synchronization

问题:当显示器已经锁定时重新进入同步块的成本是多少?

例如:

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

void innerOne() { synchronized (lock) { /* ... */ } }
void innerTwo() { synchronized (lock) { /* ... */ } }

上述目的是在innerOne上同步线程时始终调用innerTwolock

如果存在不可忽略的成本,是否可以调用任何方法来放入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操作系统上)?

直观地说,我不相信重新进入同步块将会有明显的成本,因为我发现的大多数文章都暗示无竞争锁是便宜的。但是,在这些方法中添加同步块感觉不正确,因为它给人的印象是它们可以被称为而没有首先在锁上同步。断言提供了更好的意图,但它看起来像一个相当丑陋的黑客。我想知道是否有充分理由认为没有更合法的选择。

2 个答案:

答案 0 :(得分:4)

所以你的问题是问一件事,然后你的问题正在问别的问题。我当然可以说你的算法来测试一个线程是否持有锁定会非常慢。比不检查慢得多。抛出这样的例外是很昂贵的。关于例外的第一条规则。 不要在程序中使用例外进行常规流量控制。异常一种控制流结构形式,但您只应将它们用于错误流控制。主要是因为无论何时抛出异常,它都必须收集堆栈跟踪以将其放入异常中。这是一项昂贵的操作。您绝对应该使用异常,但仅限于它们的用途:错误流控制。

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

http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#holdsLock(java.lang.Object

至于为currentThread重新进入同步块的成本是多少?我没有非常详细的答案。我怀疑无论调用holdLock的成本是多少。这可能是第一次检查,如果它返回false,那么请求将此线程添加到等待此对象监视器的线程列表上会有更多成本。如果这是真的,它会直接进入区块。 Java线程,如果它想要高效,那么对于这个问题的答案最好是锁定所有者的快速。对于不拥有锁的线程,它肯定更昂贵。如果对确切算法的答案有答案,我可以在“Taming Threads”一书或Java规范中打赌。

答案 1 :(得分:2)

测量它。在我的系统上可能没有区别。在你的可能上。您部署的系统可能会更加不同。