多线程多锁似乎比单锁慢
Multithreading multiple locks seem slower than single lock
我正在阅读 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
am i wrong to think so? if no, why am i getting the below strange result?
不,你没有看错。如果您使用 2 个锁,则 2 个线程可以同时执行 2 个不同的块。
您所看到的很可能是因为这些细粒度、线程化的性能测试非常微妙。这可能与分支和锁定预测有关,而不是与真实 运行 时间有关。 JVM 猜测是否应该在自旋循环和真正的互斥锁中测试锁 wait/queue。考虑到实际工作只是 ++,它可以显着优化此代码。也可能是 t1.start()
开始并且 运行 太快以至于它在 t2.start()
可以执行之前完成。
如果您将锁体更改得更坚固,您应该会开始看到这 2 个锁会导致更快的挂钟 运行时间。例如,如果您在块中执行循环 ++ 操作:
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 在 运行 的前几秒甚至几分钟内进行了一大堆 class 加载、gcc -O3 等价和机器代码内联。每当您尝试 运行 Java 性能测试时,您需要确保您的程序 运行 长时间运行以获得一定程度的准确数字。
我正在阅读 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
am i wrong to think so? if no, why am i getting the below strange result?
不,你没有看错。如果您使用 2 个锁,则 2 个线程可以同时执行 2 个不同的块。
您所看到的很可能是因为这些细粒度、线程化的性能测试非常微妙。这可能与分支和锁定预测有关,而不是与真实 运行 时间有关。 JVM 猜测是否应该在自旋循环和真正的互斥锁中测试锁 wait/queue。考虑到实际工作只是 ++,它可以显着优化此代码。也可能是 t1.start()
开始并且 运行 太快以至于它在 t2.start()
可以执行之前完成。
如果您将锁体更改得更坚固,您应该会开始看到这 2 个锁会导致更快的挂钟 运行时间。例如,如果您在块中执行循环 ++ 操作:
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 在 运行 的前几秒甚至几分钟内进行了一大堆 class 加载、gcc -O3 等价和机器代码内联。每当您尝试 运行 Java 性能测试时,您需要确保您的程序 运行 长时间运行以获得一定程度的准确数字。