gpt4 book ai didi

Java 同步 : just keep critical sections small?

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:45:16 25 4
gpt4 key购买 nike

我正在做一些基本实验来评估同步块(synchronized block)的开销。我对结果感到很困惑,因此提出了这个问题。

在下面的代码中,多个线程在所谓的临界区中测试并递增全局计数器(达到目标数)。此外,可以在关键部分内部或外部执行额外的可配置工作负载。

使用常量,特别是 LOAD_OF_WORK_OUTSIDE_CRITICAL_SECTIONLOAD_OF_WORK_INSIDE_CRITICAL_SECTION,我观察到的是,同步块(synchronized block)引入的开销只有大关键部分内的工作量。查看这两个输出示例:

Processors: 4
NUM_OF_THREADS: 4
LOAD_OF_WORK_OUTSIDE_CRITICAL_SECTION: 1000000
LOAD_OF_WORK_INSIDE_CRITICAL_SECTION: 100
NUM_OF_JOBS_GOAL: 10000
Non synchronized - Goal reached, elapsed time: 6370 milliseconds.
Synchronized - Goal reached, elapsed time: 6355 milliseconds.

Processors: 4
NUM_OF_THREADS: 4
LOAD_OF_WORK_OUTSIDE_CRITICAL_SECTION: 100
LOAD_OF_WORK_INSIDE_CRITICAL_SECTION: 1000000
NUM_OF_JOBS_GOAL: 10000
Non synchronized - Goal reached, elapsed time: 6351 milliseconds.
Synchronized - Goal reached, elapsed time: 18629 milliseconds.

如您所见,同步开销似乎只发生在高 LOAD_OF_WORK_INSIDE_CRITICAL_SECTION 时。这本身并不令人困惑,当然,这也证实了保持关键部分较小是一种很好的做法。但是考虑到,对于良好的实践来说,拥有大的关键部分并不常见,这个结果与尽可能避免在代码中使用 synchronized 关键字的普遍观点相冲突。相反,我会说同步关键字对于关键部分中的少量工作始终是安全的。

所以我担心我做错了什么,无论是在我的代码中还是在我的脑海中。你能帮我澄清一下吗?

下面是我用于测试的代码。谢谢,抱歉我的英语不好。

最好的问候,约翰

操作系统:Windows 7Java版本:1.7.0_67(32位)

public class MainClass {

public static void main(String[] args) throws Exception {

long startMilliseconds = System.currentTimeMillis();
final long NUM_OF_JOBS_GOAL = 10000L;
final int LOAD_OF_WORK_OUTSIDE_CRITICAL_SECTION = 1000000;
final int LOAD_OF_WORK_INSIDE_CRITICAL_SECTION = 100;
final int NUM_OF_THREADS = Runtime.getRuntime().availableProcessors();

System.out.println("Processors: " + Runtime.getRuntime().availableProcessors());
System.out.println("NUM_OF_THREADS: " + NUM_OF_THREADS);
System.out.println("LOAD_OF_WORK_OUTSIDE_CRITICAL_SECTION: " + LOAD_OF_WORK_OUTSIDE_CRITICAL_SECTION);
System.out.println("LOAD_OF_WORK_INSIDE_CRITICAL_SECTION: " + LOAD_OF_WORK_INSIDE_CRITICAL_SECTION);
System.out.println("NUM_OF_JOBS_GOAL: " + NUM_OF_JOBS_GOAL);

doConcurrentJob(NUM_OF_THREADS, startMilliseconds, NUM_OF_JOBS_GOAL, LOAD_OF_WORK_OUTSIDE_CRITICAL_SECTION, LOAD_OF_WORK_INSIDE_CRITICAL_SECTION);

//Reset state
startMilliseconds = System.currentTimeMillis();
CounterThread.goalGlobalCounter = 0;
CounterThread.goalReached = false;

doConcurrentSynchronizedJob(NUM_OF_THREADS, startMilliseconds, NUM_OF_JOBS_GOAL, LOAD_OF_WORK_OUTSIDE_CRITICAL_SECTION, LOAD_OF_WORK_INSIDE_CRITICAL_SECTION);

}

static void doConcurrentJob(int numOfThreads, long startMilliseconds, long numOfJobsGoal, int loadOfWorkOutsideCriticalSection, int loadOfWorkInsideCriticalSection) throws Exception {
CounterThread[] counterThreads = new CounterThread[numOfThreads];
while (!CounterThread.goalReached) {
for (int i = 0; i < counterThreads.length; i++) {
if (counterThreads[i] == null || !counterThreads[i].isAlive()) {
counterThreads[i] = new CounterThread(numOfJobsGoal, loadOfWorkOutsideCriticalSection, loadOfWorkInsideCriticalSection);
counterThreads[i].start();
}
}
}
System.out.println("Non synchronized - Goal reached, elapsed time: " + (System.currentTimeMillis() - startMilliseconds) + " milliseconds.");
System.out.flush();
for (int i = 0; i < counterThreads.length; i++) {
counterThreads[i].join();
}
}

static void doConcurrentSynchronizedJob(int numOfThreads, long startMilliseconds, long numOfJobsGoal, int loadOfWorkOutsideCriticalSection, int loadOfWorkInsideCriticalSection) throws Exception {
CounterThreadSynchronized[] counterThreadsSyncronized = new CounterThreadSynchronized[numOfThreads];
while (!CounterThread.goalReached) {
for (int i = 0; i < counterThreadsSyncronized.length; i++) {
if (counterThreadsSyncronized[i] == null || !counterThreadsSyncronized[i].isAlive()) {
counterThreadsSyncronized[i] = new CounterThreadSynchronized(startMilliseconds, numOfJobsGoal, loadOfWorkOutsideCriticalSection, loadOfWorkInsideCriticalSection);
counterThreadsSyncronized[i].start();
}
}
}
System.out.println("Synchronized - Goal reached, elapsed time: " + (System.currentTimeMillis() - startMilliseconds) + " milliseconds.");
System.out.flush();
for (int i = 0; i < counterThreadsSyncronized.length; i++) {
counterThreadsSyncronized[i].join();
}
}
}

class CounterThread extends Thread {

public static int goalGlobalCounter = 0;
public static boolean goalReached;

public final long GOAL;
protected final int LOAD_OF_WORK_OUTSIDE_CRITICAL_SECTION;
private final int LOAD_OF_WORK_INSIDE_CRITICAL_SECTION;

protected int fooSpinner;

public CounterThread(long numOfJobsGoal, int loadOfWorkOutsideCriticalSection, int loadOfWorkInsideCriticalSection) {
this.GOAL = numOfJobsGoal;
this.LOAD_OF_WORK_OUTSIDE_CRITICAL_SECTION = loadOfWorkOutsideCriticalSection;
this.LOAD_OF_WORK_INSIDE_CRITICAL_SECTION = loadOfWorkInsideCriticalSection;
}

public void run() {
for (long i = 0; i < LOAD_OF_WORK_OUTSIDE_CRITICAL_SECTION; i++) {
fooSpinner++;
}
executeCriticalSection();
}

public void executeCriticalSection() {

for (long i = 0; i < LOAD_OF_WORK_INSIDE_CRITICAL_SECTION; i++) {
fooSpinner++;
}
if (goalGlobalCounter < GOAL) {
goalGlobalCounter++;
} else {
goalReached = true;

}
}

}

class CounterThreadSynchronized extends CounterThread {

protected static final Object globalMutex = new Object();

public CounterThreadSynchronized(long startMilliseconds, long numOfJobsGoal, int loadOfWorkOutsideCriticalSection, int loadOfWorkInsideCriticalSection) {
super(numOfJobsGoal, loadOfWorkOutsideCriticalSection, loadOfWorkInsideCriticalSection);
}

@Override
public void run() {
for (long i = 0; i < LOAD_OF_WORK_OUTSIDE_CRITICAL_SECTION; i++) {
fooSpinner++;
}
synchronized (globalMutex) {
executeCriticalSection();
}
}
}

编辑

Mike Nakis:我复制粘贴了您的代码,结果与您的不同。以下是 TEST_DURATION = 1000 的 10 次连续运行的日志。

Processors: 4 Threads: 4
Outside | Inside | Locking | Work Done
10000 | 10 | false | 494682730
10000 | 10 | true | 515156056
10 | 10000 | false | 520437287
10 | 10000 | true | 135192560
10 | 10 | false | 499448540
10 | 10 | true | 64254608
Done.

Processors: 4 Threads: 4
Outside | Inside | Locking | Work Done
10000 | 10 | false | 519790639
10000 | 10 | true | 507597477
10 | 10000 | false | 520784275
10 | 10000 | true | 133563124
10 | 10 | false | 510318548
10 | 10 | true | 66006750
Done.

Processors: 4 Threads: 4
Outside | Inside | Locking | Work Done
10000 | 10 | false | 512302804
10000 | 10 | true | 514999373
10 | 10000 | false | 526430883
10 | 10000 | true | 132596432
10 | 10 | false | 506235601
10 | 10 | true | 66220700
Done.

Processors: 4 Threads: 4
Outside | Inside | Locking | Work Done
10000 | 10 | false | 505257231
10000 | 10 | true | 512668300
10 | 10000 | false | 528309859
10 | 10000 | true | 133947238
10 | 10 | false | 518984983
10 | 10 | true | 63617110
Done.

Processors: 4 Threads: 4
Outside | Inside | Locking | Work Done
10000 | 10 | false | 522235388
10000 | 10 | true | 502896342
10 | 10000 | false | 515668568
10 | 10000 | true | 130705136
10 | 10 | false | 514470943
10 | 10 | true | 60617050
Done.

Processors: 4 Threads: 4
Outside | Inside | Locking | Work Done
10000 | 10 | false | 517828858
10000 | 10 | true | 515355048
10 | 10000 | false | 512963551
10 | 10000 | true | 134235958
10 | 10 | false | 515017236
10 | 10 | true | 62228490
Done.

Processors: 4 Threads: 4
Outside | Inside | Locking | Work Done
10000 | 10 | false | 521690615
10000 | 10 | true | 527830725
10 | 10000 | false | 512735126
10 | 10000 | true | 134278503
10 | 10 | false | 507281283
10 | 10 | true | 63333950
Done.

Processors: 4 Threads: 4
Outside | Inside | Locking | Work Done
10000 | 10 | false | 515604517
10000 | 10 | true | 529685270
10 | 10000 | false | 520260430
10 | 10000 | true | 131993844
10 | 10 | false | 505190996
10 | 10 | true | 66865140
Done.

Processors: 4 Threads: 4
Outside | Inside | Locking | Work Done
10000 | 10 | false | 522747273
10000 | 10 | true | 530824975
10 | 10000 | false | 536263165
10 | 10000 | true | 131938210
10 | 10 | false | 502281027
10 | 10 | true | 64480710
Done.

Processors: 4 Threads: 4
Outside | Inside | Locking | Work Done
10000 | 10 | false | 523386208
10000 | 10 | true | 511467042
10 | 10000 | false | 512778324
10 | 10000 | true | 133751262
10 | 10 | false | 513257782
10 | 10 | true | 61573350
Done.

正如我的问题标题所暗示的,我主要对低“内部”/“外部”比率感兴趣,也就是说输出的前两个配置。查看输出,不能老实说锁定在任何情况下都比非锁定慢。

最佳答案

这真的取决于“小”的定义因问题而异。幸运的是有 Amdahl's law让你清楚这一点。

Amdahl's law states that if P is the proportion of a program that can be made parallel, and (1 − P) is the proportion that cannot be parallelized, then the maximum speedup that can be achieved by using N processors is S(N) = 1 / ((1-P) + P/N)

“关键 session ”将构成“无法并行化的部分”,因此您创建它的时间越长,您可能通过并行化实现的潜在吞吐量增益就越低。

在实践中,这并不是那么明确。例如,锁定的开销可能大于理论增益。出于这个原因,JVM 有时会执行“锁粗化”,这实际上会使临界区变长,但会减少总体开销。

关于Java 同步 : just keep critical sections small?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28428734/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com