Java 如何断言该方法不是并发运行的(或者在并发运行时快速失败)?

Java 如何断言该方法不是并发运行的(或者在并发运行时快速失败)?,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,当多个线程进入一个已知不安全的方法时,有没有一种快速失败的方法 编辑:假设一个方法是外部同步的,不应该并发运行。然而,如果外部同步由于某种原因失败,最好尽快失败,从而避免微妙的竞争条件问题。此外,由于该方法通常仅在单个线程中运行,因此最好避免/最小化检查的同步惩罚。您可以创建一个锁和包装器方法,然后让每个调用方调用该方法 private final Lock lock = new ReentrantLock(); public void wrapperMethod() { if (!lo

当多个线程进入一个已知不安全的方法时,有没有一种快速失败的方法


编辑:假设一个方法是外部同步的,不应该并发运行。然而,如果外部同步由于某种原因失败,最好尽快失败,从而避免微妙的竞争条件问题。此外,由于该方法通常仅在单个线程中运行,因此最好避免/最小化检查的同步惩罚。

您可以创建一个锁和包装器方法,然后让每个调用方调用该方法

private final Lock lock = new ReentrantLock();
public void wrapperMethod() {
    if (!lock.tryLock())
        throw new RuntimeException()
    try {
        threadUnsafeMethod();
    }
    finally {
        lock.unlock();
    }
}
使用
tryLock
调用方尝试立即获取锁。如果锁已经被其他调用方获取,它将返回
false
,并抛出异常

如果要使每个调用方在并发调用时快速失败,则意味着没有两个线程同时访问该方法。否则,两个线程中的一个肯定失败了。这样,您可以有效地为方法添加线程安全性

使用原子长度的等效方法,但仍然是一种锁定机制:

AtomicLong threadId = new AtomicLong(-1);
public void wrapperMethod() {
    threadId.compareAndSet(-1, Thread.currentThread().getId());
    if (threadId.get() != Thread.currentThread().getId())
        throw new RuntimeException();
    try {
        threadUnsafeMethod();
    }
    finally {
        threadId.set(-1);
    }
}
也就是说,如果您只允许使用特定的线程来运行代码,那么就可以让线程运行竞赛了。然后仅使用获胜者运行该方法:

AtomicLong winningThreadId = new AtomicLong(-1);

public void runContest() {
    winningThreadId.compareAndSet(-1, Thread.currentThread().getId());
}

public void wrapperMethod() {
    if (winningThreadId.get() != Thread.currentThread().getId())
        throw new RuntimeException();
    threadUnsafeMethod();
}

因此,每个候选线程运行一次竞赛,然后使用包装器方法。

我使用
原子布尔值。首先,我们有:

private final AtomicBoolean isExecuting = new AtomicBoolean();
然后,我们在方法中做的第一件事不应该同时执行:

if (isExecuting.getAndSet(true)) {
    throw new UnsupportedOperationException();
}
确保执行方法的一个线程在退出时重置标志:

try {
    // ... method implementation
}
finally {
    isExecuting.set(false);
}

您可能会看到两个真实的示例和。

这里的锁解决方案都增加了性能开销,我猜您没有因此使类线程安全。Java的集合也处于相同的情况,它们通过类中的“mod count”字段解决了这个问题。它并不完美(AtomicInteger会更好),而且也不能保证,但它可以处理大多数情况

如果你只是想保护自己,你可以这样做

public class Foo {
    private final AtomicBoolean inThreadUnsafeMethod = new AtomicBoolean();
    public void threadUnsafeMethod() {
        if (!inThreadUnsafeMethod.compareAndSet(false, true) {
            throw new ConcurrentModificationException();
        }
        try {
            ...
        } finally {
            inThreadUnsafeMethod.set(false);
        }
    }
}
使用这两种方法时,要非常小心正确处理可重入调用
this.otherThreadUnsafeMethod()不应该失败


查看
ArrayList
(搜索
modCount
)。

tryLock
的调用必须与对
unlock
的调用保持平衡。如果添加此项,则该方法将成为线程安全的。我想OP不能这么做,谁知道?知道怎么做吗?如果不引入某种同步,这将很困难……请澄清您的问题,以便我们知道您的问题是什么。试着提供一个你已经尝试过的例子,以及你在解决方案中遇到的错误或问题。你是这样做的,但是程序是如何知道的?你拥有源代码吗?你能让这个方法线程安全吗?你能解决这个问题吗?使线程成为该方法所属类的本地实例。我支持我的投票。很难理解OP试图解决的问题。如果代码是在外部同步的,那么它不会失败,也不需要进行检查。如果代码未在外部同步,则如果需要线程安全行为,则需要在内部执行完全同步。请使用
AtomicInteger
<代码>++
不是原子的。我在帖子中提到了这一点。“它不完美(原子整数更好)”它不仅不完美,而且是错误的。您可以通过修改代码片段以使用
AtomicInteger
@SotiriosDelimanolis来更正它。在任何平台上,当存在重叠调用时,此实现不保证总是抛出异常,这一事实并不表示它“错了”。这是一种妥协。这在一定程度上提高了发现错误使用该函数的机会。它的可靠性不如使用AtomicInteger实现同样的目的,但成本也没有AtomicInteger高。原子也是同步的一种形式。@SotiriosDelimanolis答案很清楚,有一种“轻”的选择,它在尽最大努力的基础上起作用,而另一种“重”的选择总是起作用。我认为它为OP想要实现的目标提供了很好的替代方案(这是模糊的)。除了OP询问如何在不引入任何新的同步的情况下实现这一点之外,我想不出为什么会出现这种情况,原子学是同步的一种形式。但是仍然不值得使用-1:它可以工作,而且完全按照OP的要求去做是不可能的。这与这里提到的modcount方法非常相似。事实上,这是我最初希望得到的选择之一。谢谢
public class Foo {
    private final AtomicBoolean inThreadUnsafeMethod = new AtomicBoolean();
    public void threadUnsafeMethod() {
        if (!inThreadUnsafeMethod.compareAndSet(false, true) {
            throw new ConcurrentModificationException();
        }
        try {
            ...
        } finally {
            inThreadUnsafeMethod.set(false);
        }
    }
}