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);
}
}
}