考虑以下代码:
void A() {
synchronized (obj) {
for (int i = 0; i < 1000; i++) {
B();
}
}
}
void B() {
synchronized (obj) {
// Do something
}
}
在调用A时“同步”的开销是多少?会接近只有一个“同步”的开销吗?
答案 0 :(得分:1)
这个(合法)问题的答案取决于操作系统,硬件和特定的VM实施。
不考虑函数调用的开销,在一个OS /体系结构上(考虑现代处理器/ OS / VM),可能几乎不花钱,而在另一个OS /体系结构上(考虑纯粹的软件处理器仿真),它的花费可能更多。在单个绿色线程VM上,它的成本可能接近于零(调用开销除外)。即使在具有同等性能的ARM和Intel之间,成本也会有所不同。
synchronized()通常是通过使用OS同步原语在VM内部实现的,并使用一些试探法来加速常见情况。操作系统依次使用硬件指令和启发式方法执行此任务。通常,随后获取已经获取的同步原语在OS中非常有效,而在典型的生产级VM上非常高效。
通常,在现代的Windows / Linux VM和Intel / AMD处理器上,它不需要花费很多CPU周期(假设闲置的机器是闲置的),并且在低纳秒范围内。
注意,通常,这是一个非常复杂的主题。涉及多层软件,硬件(以及在相同硬件资源上运行的其他任务的影响)。对此处很小的一个子主题的严格研究都可以组成多个博士学位。论文。
但是,在实践中,我的建议是假设在小循环中每秒同步的成本为零,除非您遇到特定的瓶颈(这不太可能发生)。
如果存在大量迭代,则与单次同步相比,无疑会增加成本,并且总体效果取决于您在循环内所做的工作。通常,每次迭代都需要做一些工作,使得相对开销可以忽略不计。但是在某些情况下,它可能会阻止循环优化并增加大量开销(与单次同步相比可观,而不是实际的措施)。但是,在常见的大循环实际情况下,应该考虑不同的设计,并避免执行外部同步以减少lock contention。
要了解VM的实现,您可以查看this paper的“同步”部分。这有点过时,但很容易理解。
答案 1 :(得分:0)
synchronized
锁是可重入的,并且在线程已经持有锁的情况下获得锁是:a)检查它是否已经锁住的时间,b)递增计数器并随后递减的时间
第一个耗时最长,每次加约10-50 ns。