我有一个类序列化程序:
class Serializer<T> extends Consumer<T> {
final Consumer<? super T> actual;
// constructor omitted for brewity
@Override public synchronized void accept(T t) {
actual.accept(t);
}
}其目的是确保实际的线程一次从一个线程中运行。但是,在保持锁的同时调用回调通常是危险的,因此调用方不是持有锁,而是将传入的值排队,其中一个线程将进入,释放队列并依次调用循环中的实际使用者。(另一个限制是不知道并发呼叫者的数量。)
final ConcurrentLinkedQueue<T> queue;
final AtomicInteger wip;
@Override public void accept(T t) {
queue.offer(t);
if (wip.getAndIncrement() == 0) {
do {
actual.accept(queue.poll());
} while (wip.decrementAndGet() > 0);
}
}这是可行的,并将无界队列、线程跳转和线程卡在循环中的问题置之不理,但与直接方法调用相比,基准测试在单线程情况下提供了10%的吞吐量。当I.用同步块实现这个排队/发射时,基准测试给出了50%的直接情况,因为JVM优化了同步化;这将是很棒的,但它也不会扩展。使用juc.Lock扩展,但遭受类似的单线程吞吐量下降与上述代码。如果我没有弄错,一旦JVM优化了同步,它仍然需要使用一些保护,以防再次并发调用该方法并将锁定放回。
我的问题是,如何使用锁、队列或其他序列化逻辑实现类似的效果,即,在没有并发调用的情况下,拥有一条廉价且快速的路径,而另一条路径则适用于同意的use情况,这样代码就可以扩展,并且对于单线程的使用也是快速的。
发布于 2014-05-19 10:20:46
正如其他人所说的,synchronized已经为您提供了一个相当有效的工具。因此,使用不同的东西不应该被期望更好的性能所激励。
但是,如果你的意图不是像你在问题中所说的那样阻止打电话的人,那是合法的。(虽然这可能意味着与较低的表现生活)。
在查看使用队列和原子整数的尝试时,我首先看到的是,在没有挂起的项和没有其他使用者正在运行的情况下,可以绕过队列。在争用较低的情况下,这可能会减少开销:
final ConcurrentLinkedQueue<T> queue;
final AtomicInteger wip;
@Override public void accept(T t) {
if(wip.compareAndSet(0, 1)) { // no contention?
actual.accept(t);
if(wip.decrementAndGet()==0) return; // still no contention
}
else {
if(!queue.offer(t))
throw new AssertionError("queue should be unbounded");
if(wip.getAndIncrement() != 0) return; // other consumer running
}
do {
actual.accept(queue.poll());
} while (wip.decrementAndGet() > 0);
}发布于 2014-05-18 17:26:29
这就是同步为您所做的。(除其他策略外,它还使用了偏置锁定)
如果你问的是,是否有一种更有效的方法来对数据进行一般的同步,那么如果你能想到这方面几十年的专业知识,我会感到非常惊讶。如果您确实找到了一种更快的方式,您可以出名,这可能是"kd304锁定策略“。
如果您是在问是否有方法减少特定/特殊情况下代码段的开销,那么肯定有,但这取决于您正在做什么,这在问题中并不清楚。
https://stackoverflow.com/questions/23724249
复制相似问题