如何断言该方法不是 运行 并发(或快速失败)?
How to assert that method is not run concurrently (or fail-fast when it is)?
有没有一种方法可以在多个线程进入已知不是线程安全的方法时快速失败?
编辑:假设一个方法是外部同步的,不应该同时 运行。但是,如果外部同步由于某种原因失败,最好尽快失败,从而避免微妙的竞争条件问题。此外,由于该方法通常 运行 仅在单个线程中使用,因此对 avoid/minimize 检查的同步惩罚非常有用。
你可以创建一个锁和一个包装器方法,然后你可以让每个调用者调用这个方法
private final Lock lock = new ReentrantLock();
public void wrapperMethod() {
if (!lock.tryLock())
throw new RuntimeException()
try {
threadUnsafeMethod();
}
finally {
lock.unlock();
}
}
对于tryLock
,调用者试图立即获取锁。如果锁已经被其他调用者获取 returns false
并且我们抛出异常。
如果你想让 each 调用者在并发调用的情况下快速失败,那么这意味着没有两个线程同时访问该方法。否则,这两个线程之一一定失败了。这样您就可以有效地为您的方法添加线程安全。
一个使用原子 long 的等效方法,但它仍然是一个锁定机制:
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);
}
}
也就是说,如果您只允许使用特定的线程来 运行 代码,这就给了线程 运行 竞赛的想法。然后仅使用获胜者进行 运行ning 方法:
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();
}
所以每个候选线程 运行 都会参加一次比赛,然后使用包装方法。
我使用 AtomicBoolean
。首先我们有:
private final AtomicBoolean isExecuting = new AtomicBoolean();
然后,我们在不应该同时执行的方法中做的第一件事:
if (isExecuting.getAndSet(true)) {
throw new UnsupportedOperationException();
}
确保执行您的方法的线程在退出时重置标志:
try {
// ... method implementation
}
finally {
isExecuting.set(false);
}
此处的锁解决方案都会增加性能开销,我猜您之所以没有采用 class thread-safe 是因为这个原因。 Java的collections也是同样的情况,他们用class中的"mod count"字段解决了。它并不完美(AtomicInteger 会更好),并且不能保证,但它捕获了 大多数 个案例。
public class Foo {
private volatile int modCount = 0;
public void threadUnsafeMethod() {
int startModCount = ++modCount;
...
if (modCount != startModCount) { throw new ConcurrentModificationException(); }
}
}
如果你只是想守护,可以
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
implementation(搜索 modCount
)。
有没有一种方法可以在多个线程进入已知不是线程安全的方法时快速失败?
编辑:假设一个方法是外部同步的,不应该同时 运行。但是,如果外部同步由于某种原因失败,最好尽快失败,从而避免微妙的竞争条件问题。此外,由于该方法通常 运行 仅在单个线程中使用,因此对 avoid/minimize 检查的同步惩罚非常有用。
你可以创建一个锁和一个包装器方法,然后你可以让每个调用者调用这个方法
private final Lock lock = new ReentrantLock();
public void wrapperMethod() {
if (!lock.tryLock())
throw new RuntimeException()
try {
threadUnsafeMethod();
}
finally {
lock.unlock();
}
}
对于tryLock
,调用者试图立即获取锁。如果锁已经被其他调用者获取 returns false
并且我们抛出异常。
如果你想让 each 调用者在并发调用的情况下快速失败,那么这意味着没有两个线程同时访问该方法。否则,这两个线程之一一定失败了。这样您就可以有效地为您的方法添加线程安全。
一个使用原子 long 的等效方法,但它仍然是一个锁定机制:
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);
}
}
也就是说,如果您只允许使用特定的线程来 运行 代码,这就给了线程 运行 竞赛的想法。然后仅使用获胜者进行 运行ning 方法:
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();
}
所以每个候选线程 运行 都会参加一次比赛,然后使用包装方法。
我使用 AtomicBoolean
。首先我们有:
private final AtomicBoolean isExecuting = new AtomicBoolean();
然后,我们在不应该同时执行的方法中做的第一件事:
if (isExecuting.getAndSet(true)) {
throw new UnsupportedOperationException();
}
确保执行您的方法的线程在退出时重置标志:
try {
// ... method implementation
}
finally {
isExecuting.set(false);
}
此处的锁解决方案都会增加性能开销,我猜您之所以没有采用 class thread-safe 是因为这个原因。 Java的collections也是同样的情况,他们用class中的"mod count"字段解决了。它并不完美(AtomicInteger 会更好),并且不能保证,但它捕获了 大多数 个案例。
public class Foo {
private volatile int modCount = 0;
public void threadUnsafeMethod() {
int startModCount = ++modCount;
...
if (modCount != startModCount) { throw new ConcurrentModificationException(); }
}
}
如果你只是想守护,可以
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
implementation(搜索 modCount
)。