【发布时间】:2011-04-03 03:30:51
【问题描述】:
我正在实现请求实例的 FIFO 队列(为速度而预先分配的请求对象),并开始使用 add 方法上的“同步”关键字。该方法很短(检查固定大小缓冲区中是否有空间,然后将值添加到数组)。使用 visualVM 时,线程似乎比我喜欢的更频繁地阻塞(准确地说是“监视器”)。因此,我将代码转换为使用 AtomicInteger 值来跟踪当前大小,然后在 while 循环中使用 compareAndSet()(就像 AtomicInteger 在内部对诸如 incrementAndGet() 之类的方法所做的那样)。代码现在看起来更长了。
我想知道的是,使用同步且较短的代码与不带 synchronized 关键字的较长代码相比,性能开销是多少(因此永远不应阻塞锁定)。
这是带有 synchronized 关键字的旧 get 方法:
public synchronized Request get()
{
if (head == tail)
{
return null;
}
Request r = requests[head];
head = (head + 1) % requests.length;
return r;
}
这里是没有 synchronized 关键字的新 get 方法:
public Request get()
{
while (true)
{
int current = size.get();
if (current <= 0)
{
return null;
}
if (size.compareAndSet(current, current - 1))
{
break;
}
}
while (true)
{
int current = head.get();
int nextHead = (current + 1) % requests.length;
if (head.compareAndSet(current, nextHead))
{
return requests[current];
}
}
}
我的猜测是 synchronized 关键字更糟,因为有阻塞锁的风险(可能导致线程上下文切换等),即使代码更短。
谢谢!
【问题讨论】:
-
你知道,这整个事情可能不是问题,因为对象分配在像 HotSpot 这样的现代 JVM 中非常非常便宜(不过,Dalvik 和 JME VM 可能是另一回事)。你有没有在没有对象池的情况下测试过这个速度?
-
我做了性能比较。我必须每秒处理 300 万个请求,因此每秒创建 300 万个对象(还不错)加上 300 万个要被垃圾收集的对象。它是实时的 - 我必须跟上或丢失数据。我现在已经获得了每个请求(使用预分配池)创建零对象的代码,并且在双四核机器上获得的代码几乎足够快。预分配也意味着一旦系统启动,我永远不会耗尽内存。
标签: java locking compare-and-swap