我在高并发应用上工作。在应用程序代码中,我尝试尽可能避免同步。最近,在比较不同步和同步代码版本的测试性能时,结果表明同步代码的执行速度比非同步代码快三到四倍。
经过一些实验后,我来到了这个测试代码:
private static final Random RND = new Random();
private static final int NUM_OF_THREADS = 3;
private static final int NUM_OF_ITR = 3;
private static final int MONKEY_WORKLOAD = 50000;
static final AtomicInteger lock = new AtomicInteger();
private static void syncLockTest(boolean sync) {
System.out.println("syncLockTest, sync=" + sync);
final AtomicLong jobsDone = new AtomicLong();
final AtomicBoolean stop = new AtomicBoolean();
for (int i = 0; i < NUM_OF_THREADS; i++) {
Runnable runner;
if (sync) {
runner = new Runnable() {
@Override
public void run() {
while (!stop.get()){
jobsDone.incrementAndGet();
synchronized (lock) {
monkeyJob();
}
Thread.yield();
}
}
};
} else {
runner = new Runnable() {
@Override
public void run() {
while (!stop.get()){
jobsDone.incrementAndGet();
monkeyJob();
Thread.yield();
}
}
};
}
new Thread(runner).start();
}
long printTime = System.currentTimeMillis();
for (int i = 0; i < NUM_OF_ITR;) {
long now = System.currentTimeMillis();
if (now - printTime > 10 * 1000) {
printTime = now;
System.out.println("Jobs done\t" + jobsDone);
jobsDone.set(0);
i++;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
stop.set(true);
}
private static double[] monkeyJob() {
double[] res = new double[MONKEY_WORKLOAD];
for (int i = 0; i < res.length; i++) {
res[i] = RND.nextDouble();
res[i] = 1./(1. + res[i]);
}
return res;
}
我使用线程数,工作负载,测试迭代次数 - 每次同步代码的执行速度比非中心代码快得多。
以下是NUM_OF_THREADS
的两个不同值的结果线程数:3
syncLockTest,sync = true
工作 完成5951
工作完成5958
工作完成5878
syncLockTest, sync = false
工作完成1399
工作完成1397
工作 完成1391
线程数:5
syncLockTest,sync = true
作业 完成5895
工作完成6464
工作完成5886
syncLockTest, sync = false
完成工作1179
完成工作1260
工作 完成了1226年
测试环境 Windows 7专业版 Java 7.0版
这是一个类似案例Synchronized code performs faster than unsynchronized one
有什么想法吗?
答案 0 :(得分:8)
Random是一个线程安全的类。您最有可能通过在主要作业周围进行同步来避免对Random类调用的争用。
答案 1 :(得分:4)
这很吸引人。我想@jtahlborn坚持了它。如果我移动Random
并使其成为本地线程,则非同步跳转的时间约为10x而synchronized
的时间不会改变:
以下是我static Random RND
的时间:
syncLockTest, sync=true
Jobs done 8800
Jobs done 8839
Jobs done 8896
syncLockTest, sync=false
Jobs done 1401
Jobs done 1381
Jobs done 1423
以下是我每次使用Random rnd
局部变量的时间:
syncLockTest, sync=true
Jobs done 8846
Jobs done 8861
Jobs done 8866
syncLockTest, sync=false
Jobs done 25956 << much faster
Jobs done 26065 << much faster
Jobs done 26021 << much faster
我也想知道这是否与GC相关,但将double[] res
移动到本地线程并没有帮助提高速度。这是我使用的代码:
...
@Override
public void run() {
// made this be a thread local but it affected the times only slightly
double[] res = new double[MONKEY_WORKLOAD];
// turned rnd into a local variable instead of static
Random rnd = new Random();
while (!stop.get()) {
jobsDone.incrementAndGet();
if (sync) {
synchronized (lock) {
monkeyJob(res, rnd);
}
} else {
monkeyJob(res, rnd);
}
}
}
...
private static double[] monkeyJob(double[] res, Random rnd) {
for (int i = 0; i < res.length; i++) {
res[i] = rnd.nextDouble();
res[i] = 1. / (1. + res[i]);
}
return res;
}