【发布时间】:2018-11-07 19:20:46
【问题描述】:
我有两个必须在临界区运行的函数:
public synchronized void f1() { ... }
public synchronized void f2() { ... }
假设行为如下:
-
f1几乎从不被调用。实际上,在正常情况下,这个方法永远不会被调用。如果无论如何调用f1,它应该很快返回。 -
f2的调用率非常高。它很快就会返回。 - 这些方法从不相互调用,也没有重入。
换句话说,竞争非常低。所以当f2 被调用时,我们有一些开销来获取锁,在 99.9% 的情况下会立即授予。我想知道是否有办法避免这种开销。
我想出了以下替代方案:
private final AtomicInteger lock = new AtomicInteger(0);
public void f1() {
while (!lock.compareAndSet(0, 1)) {}
try {
...
} finally {
lock.set(0);
}
}
public void f2() {
while (!lock.compareAndSet(0, 2)) {}
try {
...
} finally {
lock.set(0);
}
}
还有其他方法吗? java.util.concurrent 包本身是否提供了一些东西?
更新
虽然我的意图是提出一个笼统的问题,但关于我的情况的一些信息:
f1:如果由于某种原因当前的远程流损坏,例如由于超时,此方法会创建一个新的远程流。远程流可以被认为是一个套接字连接,它消耗从给定位置开始的远程队列:
private Stream stream;
public synchronized void f1() {
final Stream stream = new Stream(...);
if (this.stream != null) {
stream.setPosition(this.stream.getPosition());
}
this.stream = stream;
return stream;
}
f2:此方法推进流位置。这是一个简单的设置器:
public synchronized void f2(Long p) {
stream.setPosition(p);
}
这里,stream.setPosition(Long) 也被实现为一个普通的 setter:
public class Stream {
private volatile Long position = 0;
public void setPosition(Long position) {
this.position = position;
}
}
在Stream中,当前位置会定期异步发送到服务器。注意Stream不是我自己实现的。
我的想法是引入如上图所示的比较和交换,并将stream标记为volatile。
【问题讨论】:
-
为什么要同步?什么资源被同时访问?这是你应该关注的
-
@JeanLogeart 我遵循您的思考过程,但我想从问题中省略这一点。也就是说,认为这是一个通用/教育问题。
-
那些
while循环变成繁忙循环,直到锁定值被更新。分析是否显示瓶颈? -
@AndrewS 在这个特定场景中,
f2中的while循环几乎总是if。同样,这是一个通用问题。 -
如果要互斥,
synchonized是更好更自然的选择。您最好阻塞线程并且不消耗任何 CPU,而不是燃烧 CPU 周期来检查您是否不再被阻塞。这些循环可用于其他线程的计算。
标签: java concurrency java.util.concurrent