我正在尝试了解线程和同步。我做了这个测试程序:
public class Test {
static List<Thread> al = new ArrayList<>();
public static void main(String[] args) throws IOException, InterruptedException {
long startTime = System.currentTimeMillis();
al.add(new Thread(() -> fib1(47)));
al.add(new Thread(() -> fib2(47)));
for (Thread t : al)
t.start();
for (Thread t: al)
t.join();
long totalTime = System.currentTimeMillis() - startTime;
System.out.println(totalTime);
}
public static synchronized int fib1(int x) {
return x <= 2 ? 1 : fib1(x-2) + fib1(x-1);
}
public static synchronized int fib2(int x) {
return x <= 2 ? 1 : fib2(x-2) + fib2(x-1);
}
}
该程序大约需要 273 秒才能完成,但如果我删除这两个synchronized
相反,它会在 7 秒内运行。是什么导致了如此巨大的差异?
编辑:
我知道我正在使用一种非常慢的算法来计算斐波那契数。而且我还知道线程不共享资源,因此方法不需要同步。然而,这只是一个测试程序,我试图弄清楚如何synchronized
有效,我故意选择一个慢速算法,这样我就可以测量以毫秒为单位所花费的时间。
你的程序并没有卡住——只是速度非常慢。
这是由于两个原因:
1. 算法复杂度
正如其他人和您自己所提到的,计算斐波那契数的方式非常慢,因为它一遍又一遍地计算相同的值。使用较小的输入会将运行时间降低到合理的值。但这不是你的问题。
2. 同步
这会通过两种方式减慢你的程序:
首先,制定方法synchronized
没有必要,因为它们不会修改方法本身之外的任何内容。事实上,它可以防止两个线程同时运行,因为这些方法是static
因此可以防止两个线程同时出现在其中任何一个中。
因此,您的代码实际上仅使用一个线程,而不是两个线程。
Also synchronized
给方法增加了很大的开销,因为它需要在进入方法时获取锁 - 或者至少检查当前线程是否已经拥有锁。
这些操作非常昂贵,并且每次输入其中一种方法时都必须执行这些操作。因为 - 由于递归 - 这种情况发生了a lot,对程序性能影响极大。
有趣的是,当您仅使用单个线程运行它时,即使方法是synchronized
。
原因是 JVM 进行的运行时优化。
如果只使用一个线程,JVM 可以优化synchronized
检查离开,因为不可能有冲突。这显着减少了运行时间 - 但并不完全达到没有时的值synchronized
由于从“冷代码”开始和一些剩余的运行时检查。
另一方面,当使用 2 个线程运行时,JVM 无法进行此优化,因此留下了昂贵的资源synchronized
导致代码非常慢的操作。
顺便说一句:fib1 和 fib2 相同,删除其中一个
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)