Skip to content

witNie/java-concurrency-benchmarks

Repository files navigation

Java Concurrency Benchmarks (Dining Philosophers)

A small Gradle/Java project that runs repeatable concurrency benchmarks on six implementations of the classic Dining Philosophers problem. Each implementation (“v1” … “v6”) represents a different synchronization strategy, and the runner collects timing metrics and exports results to CSV for analysis.

What this project measures

Each run simulates N philosophers competing for N forks for a fixed duration. The benchmark tracks:

  • Meals eaten (throughput)
  • Total wait time accumulated across philosophers
  • Average wait time per meal (global)
  • Average wait time per philosopher per meal (fairness signal)

Results are printed to stdout and appended to a CSV file in results/.

The 6 versions (strategies)

The versions are listed in org.util.Versions and implemented in:

  • src/main/java/org/v1
  • src/main/java/org/v2
  • src/main/java/org/v3
  • src/main/java/org/v4
  • src/main/java/org/v5
  • src/main/java/org/v6

v1 — Naive (org.v1.Main, Versions.V1_NAIVE)

  • Uses plain Java monitors: nested synchronized(leftFork) then synchronized(rightFork).
  • Deadlock is possible (classic circular wait).

v2 — “Starving” / try-acquire checks (org.v2.Main, Versions.V2_STARVING)

  • Each fork has a Semaphore(1) and a free flag.
  • Philosophers loop using tryAcquire() on both semaphores, then check both forks are free before “locking”.
  • Can reduce deadlock likelihood, but may still show unfairness/starvation depending on scheduling/timing.

v3 — Asymmetric order (org.v3.Main, Versions.V3_ASYMMETRIC)

  • Breaks symmetry by changing acquisition order:
    • even philosophers pick forks in one order
    • odd philosophers pick forks in the reverse order
  • This removes the circular wait condition and is a common deadlock-avoidance approach.

v4 — Stochastic order (org.v4.Main, Versions.V4_STOCHASTIC)

  • Each iteration randomly chooses whether to pick up the left or right fork first.
  • Randomization reduces the chance of persistent lock-step behavior.

v5 — Arbiter / “waiter” (N−1 semaphore) (org.v5.Main, Versions.V5_ARBITER)

  • A global Semaphore(n - 1) limits the number of philosophers allowed to attempt eating concurrently.
  • Inside that permission, philosophers still synchronize on left then right fork.
  • This is a classic deadlock prevention technique.

v6 — Arbiter + fallback order (org.v6.Main, Versions.V6_ARBITER)

  • Also uses a global Semaphore(n - 1), but attempts tryAcquire() first.
  • If tryAcquire() succeeds: locks left then right.
  • If it fails: locks right then left (fallback acquisition order).
  • Mixes global limiting with asymmetric ordering behavior.

Configuration

Benchmark settings are in org.util.Config:

  • N = 100 philosophers
  • TEST_TIME = 10000 ms
  • EAT_TIME = 500 ms
  • THINK_TIME = 200 ms
  • REPETITIONS = 10 runs per execution
  • RAND_EAT_THINK_TIMES = true (randomizes eat/think sleep up to the configured max)
  • Output CSV: results/N_<N>.csv (example: results/N_100.csv)
  • DEBUG_PRINT = false

Output format (CSV)

Each repetition appends one line to the CSV via org.util.TestToCsv, roughly:

  • version, n, testTime, totalWaitTime, avgWaitTime, eaten, then one average value per philosopher

This makes it easy to compare strategies across runs (and across different values of N).

Project layout

  • src/main/java/org/v1..v6/ — benchmark implementations (each contains a Main)
  • src/main/java/org/util/ — shared utilities (config, results model, CSV exporter, etc.)
  • results/ — CSV output directory

Build & run

./gradlew build
./gradlew test

Running: each version has its own main method (e.g. org.v1.Main, org.v2.Main

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages