Java - 同步方法导致程序大规模减速

Jus*_*guy 6 java static multithreading synchronized

我正在尝试了解线程和同步.我做了这个测试程序:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这个程序大约需要273秒才能完成,但是如果我删除了synchronized它,它会在7秒内完成.是什么导致这种巨大的差异

编辑:我知道我正在使用一种非常慢的算法来计算斐波纳契数.而且我也知道线程不共享资源,因此这些方法不需要同步.然而,这只是一个测试程序,我试图弄清楚如何synchronized工作,我故意选择一个慢速算法,所以我可以测量所用的时间,以毫秒为单位.

Mar*_*inS 4

你的程序并没有卡住——只是速度非常慢。这是由于两个原因:

1. 算法复杂度

正如其他人和您自己所提到的,计算斐波那契数的方式非常慢,因为它一遍又一遍地计算相同的值。使用较小的输入会将运行时间降低到合理的值。但这不是你的问题。

2. 同步

这会通过两种方式减慢你的程序:

首先,创建方法synchronized不是必需的,因为它们不会修改方法本身之外的任何内容。事实上,它阻止两个线程同时运行,因为这些方法static因此阻止两个线程同时位于其中任何一个线程中。因此,您的代码实际上仅使用一个线程,而不是两个线程。

还给synchronized方法增加了显着的开销,因为它需要在进入方法时获取锁 - 或者至少检查当前线程是否已经拥有锁。这些操作非常昂贵,并且每次输入其中一种方法时都必须执行这些操作。由于递归的原因,这种情况经常发生,因此对程序性能产生了极大的影响。

有趣的是,当您仅使用单个线程运行它时,即使方法是synchronized. 原因是 JVM 进行的运行时优化。如果您只使用一个线程,JVM 可以优化检查,synchronized因为不会发生冲突。synchronized这会显着减少运行时间 - 但由于从“冷代码”开始和一些剩余的运行时间检查,因此并不完全达到没有它应有的值。另一方面,当使用 2 个线程运行时,JVM 无法进行此优化,因此留下了synchronized导致代码非常慢的昂贵操作。

顺便说一句:fib1 和 fib2 相同,删除其中一个