多线程多锁似乎比单锁慢



我读过Java中的多线程和使用同步块。假设我有两个不同的、独立的同步块。我可以让它们并行运行,方法是为两个同步块分别使用一个锁。但是,如果我对两个同步的块使用相同的锁,我认为在给定的时间只能运行一个。我这样想是错的吗?如果没有,为什么我会得到以下奇怪的结果?

假设我有两个独立的操作,increment1和increment2,分别由不同的线程调用。

public class AppMultipleSynchronization {
private static int counter1 = 0;
private static int counter2 = 0;
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void increment1() {
synchronized (lock1) {
counter1++;
}
}
public static void increment2() {
synchronized (lock2) {
counter2++;
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100000000; i++) {
increment1();
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100000000; i++) {
increment2();
}
}
});
long startTime = System.currentTimeMillis();
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("total time taken: " + (endTime - startTime) + "ms");
System.out.println("counter1: " + counter1 + "ncounter2: " + counter2);
}
}

正如您所看到的,我对两个增量都使用了不同的锁。要使用相同的锁,请使用完全相同的程序,在这两种情况下都替换为lock1

两锁情况下的输出:

total time taken: 13437ms
counter1: 100000000
counter2: 100000000

单锁输出:

total time taken: 5139ms
counter1: 100000000
counter2: 100000000

我这么认为错了吗?如果没有,为什么我会得到以下奇怪的结果?

不,你没有错。如果使用两个锁,那么两个线程可以同时执行两个不同的块。

您所看到的很可能是因为这些细粒度、线程化的性能测试非常微妙。这可能与分支和锁预测有关,而与真正的运行时有关。JVM猜测是否应该在旋转循环中测试锁,而不是在真正的互斥等待/队列中测试锁。考虑到实际工作只是++,它可以极大地优化此代码。也可能是CCD_ 2启动和运行得如此之快,以至于它在CCD_。

如果您将锁体更改为更实质性的,您应该开始看到这两个锁将导致更快的挂钟运行时间。例如,如果您在块中执行++操作的循环:

public static void increment1() {
synchronized (lock1) {
for (int i = 0; i < 100000000; i++) {
counter1++;
}
}
}
...
public void run() {
for (int i = 0; i < 10000; i++) {
increment1();
}
}

然后用1个锁我得到:

total time taken: 62822ms
counter1: -727379968
counter2: -727379968

但是2个锁得到:

total time taken: 30902ms
counter1: -727379968
counter2: -727379968

最后,JVM在运行时的最初几秒钟甚至几分钟内完成了一大堆类加载、gcc-O3等价和机器代码内联。任何时候,当你试图运行Java性能测试时,你都需要确保你的程序运行很长时间,以获得一定程度的准确数字。

最新更新