commit a43eac79a4e3bb10e9cdc0c3ff0da59c9b5782f3 Author: arya Date: Sun May 31 17:07:01 2026 +0330 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5291028 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +target/ +.idea/ +*.iml +*.iws +*.ipr +.settings/ +.classpath +.project +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..b9e8221 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# Advanced Multithreading Workshop + +This project contains the workshop materials for the Advanced Programming multithreading session. It has been migrated from Gradle to Maven and structured for clear delivery. + +## Project Structure + +- `src/main/java/workshop/exercises/`: Unsolved skeleton files with clear `TODO` markers. These are the templates to distribute to students and use during live coding. +- `src/main/java/workshop/solutions/`: Fully implemented reference solutions for grading and internal testing. + +## Exercises Covered + +1. **`LockWorkshop`**: Resolving race conditions on a shared mutable counter using explicit `ReentrantLock` coordination. +2. **`SynchronizedWorkshop`**: Resolving race conditions on a shared mutable counter using intrinsic Java monitors (`synchronized` block). +3. **`DeadlockPreventionWorkshop`**: Resolving a classic thread circular wait condition by enforcing global lock ordering based on unique Resource IDs. +4. **`TaylorSeries`**: Implementing high-precision math with `BigDecimal` and calculating the Taylor series for $\sin(x)$ using a thread pool (`ExecutorService`). + +## Requirements +- Java 17 or higher +- Apache Maven 3.6+ + +## How to Build and Run + +To compile the project: +```bash +mvn clean compile +``` + +To execute any of the workshop main classes (for example, the Lock Workshop solution): +```bash +mvn exec:java -Dexec.mainClass="workshop.solutions.LockWorkshopSolution" +``` diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..ca0cc46 --- /dev/null +++ b/pom.xml @@ -0,0 +1,40 @@ + + 4.0.0 + + com.university.advancedprogramming + multithreading-workshop + 1.0-SNAPSHOT + + + 17 + 17 + UTF-8 + + + + + org.junit.jupiter + junit-jupiter-api + 5.10.0 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.10.0 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + + + diff --git a/src/main/java/workshop/exercises/DeadlockPreventionWorkshop.java b/src/main/java/workshop/exercises/DeadlockPreventionWorkshop.java new file mode 100644 index 0000000..91ba6a8 --- /dev/null +++ b/src/main/java/workshop/exercises/DeadlockPreventionWorkshop.java @@ -0,0 +1,50 @@ +package workshop.exercises; + +public class DeadlockPreventionWorkshop { + public static class Resource { + private final int id; + private final String name; + + public Resource(int id, String name) { + this.id = id; + this.name = name; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + } + + public static void useResources(Resource r1, Resource r2) { + // TODO: Prevent deadlock by enforcing a consistent lock acquisition order + // Hint: Compare resource IDs and always synchronize on the resource with the smaller ID first. + + System.out.println(Thread.currentThread().getName() + " is attempting to lock " + r1.getName()); + synchronized (r1) { + System.out.println("+ " + Thread.currentThread().getName() + " locked " + r1.getName()); + try { Thread.sleep(100); } catch (InterruptedException ignored) {} + + System.out.println(Thread.currentThread().getName() + " is attempting to lock " + r2.getName()); + synchronized (r2) { + System.out.println("+ " + Thread.currentThread().getName() + " locked " + r2.getName()); + System.out.println(Thread.currentThread().getName() + " using " + r1.getName() + " and " + r2.getName()); + } + } + } + + public static void main(String[] args) { + Resource resA = new Resource(1, "ResourceA"); + Resource resB = new Resource(2, "ResourceB"); + + // Thread 1 locks A then B; Thread 2 locks B then A (causing deadlock if unordered) + Thread t1 = new Thread(() -> useResources(resA, resB), "Thread-1"); + Thread t2 = new Thread(() -> useResources(resB, resA), "Thread-2"); + + t1.start(); + t2.start(); + } +} diff --git a/src/main/java/workshop/exercises/LockWorkshop.java b/src/main/java/workshop/exercises/LockWorkshop.java new file mode 100644 index 0000000..ec24ecf --- /dev/null +++ b/src/main/java/workshop/exercises/LockWorkshop.java @@ -0,0 +1,37 @@ +package workshop.exercises; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class LockWorkshop { + public static int counter = 0; + + // TODO: Create a ReentrantLock instance to protect the critical section + + public static class MyRunnable implements Runnable { + @Override + public void run() { + int i; + for (i = 0; i < 1_000_000; i++) { + // TODO: Acquire the lock, increment the counter, and release the lock safely in a finally block + counter += 1; + } + System.out.println("Increments completed by " + Thread.currentThread().getName() + ": " + i); + } + } + + public static void main(String[] args) throws InterruptedException { + counter = 0; + + Thread thread1 = new Thread(new MyRunnable(), "Thread-1"); + Thread thread2 = new Thread(new MyRunnable(), "Thread-2"); + + thread1.start(); + thread2.start(); + + thread1.join(); + thread2.join(); + + System.out.println("Final counter value (expected 2000000): " + counter); + } +} diff --git a/src/main/java/workshop/exercises/SynchronizedWorkshop.java b/src/main/java/workshop/exercises/SynchronizedWorkshop.java new file mode 100644 index 0000000..9259d8e --- /dev/null +++ b/src/main/java/workshop/exercises/SynchronizedWorkshop.java @@ -0,0 +1,34 @@ +package workshop.exercises; + +public class SynchronizedWorkshop { + public static int counter = 0; + + // TODO: Define a lock object to prevent race condition + + public static class MyRunnable implements Runnable { + @Override + public void run() { + int i; + for (i = 0; i < 1_000_000; i++) { + // TODO: Use a synchronized block to protect the counter increment + counter += 1; + } + System.out.println("Increments completed by " + Thread.currentThread().getName() + ": " + i); + } + } + + public static void main(String[] args) throws InterruptedException { + counter = 0; + + Thread thread1 = new Thread(new MyRunnable(), "Thread-1"); + Thread thread2 = new Thread(new MyRunnable(), "Thread-2"); + + thread1.start(); + thread2.start(); + + thread1.join(); + thread2.join(); + + System.out.println("Final counter value (expected 2000000): " + counter); + } +} diff --git a/src/main/java/workshop/exercises/TaylorSeries.java b/src/main/java/workshop/exercises/TaylorSeries.java new file mode 100644 index 0000000..d63a18e --- /dev/null +++ b/src/main/java/workshop/exercises/TaylorSeries.java @@ -0,0 +1,75 @@ +package workshop.exercises; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +public class TaylorSeries { + + public static class CalculateSin implements Runnable { + private final MathContext mc = new MathContext(1000); + private final BigDecimal x; + private final int n; + + public CalculateSin(BigDecimal x, int n) { + this.x = x; + this.n = n; + } + + @Override + public void run() { + // TODO: Calculate the n-th term of the Taylor series for sin(x): + // term = (-1)^n * (x^(2n+1)) / (2n+1)! + // Add the term to the global sum ensuring thread-safety. + } + + // TODO: Implement factorial(k) using BigDecimal + private BigDecimal factorial(int k) { + return BigDecimal.ZERO; + } + } + + public static BigDecimal sum = BigDecimal.ZERO; + + public static void main(String[] args) { + ExecutorService threadPool = Executors.newFixedThreadPool(4); + sum = BigDecimal.ZERO; + BigDecimal x = new BigDecimal("0.01"); + + // Submit tasks to calculate terms + for (int i = 0; i < 100; i++) { + // TODO: Submit task to the thread pool + } + + threadPool.shutdown(); + try { + threadPool.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + // Pre-calculated accurate value of sin(0.01) up to 1000 decimal places + BigDecimal accurateValue = new BigDecimal("0.009999833334166664682542438269099729038964385360169151033879" + + "1124794097345090639159659426367929614989901525182568937606738071143914781018343679925045223748779233" + + "4633395662957704288475175228160558357110105077439518716860615533070998720636987509269668842490541364" + + "2046237535076816415219593915753970609625155007149734343650140126010756472960507873872984042987441343" + + "4632784947709943715670321717675280271359744354619523360203501121465199963741173489051927920551271878" + + "0890799396861427770050764919858890678677220571952090318596147460309593993397336341626522171452145068" + + "2733933417711179372733354590095099959888961964291389175600643442999116373725594047366187408263088683" + + "4976343405988894064532908768068263525544004211332459809773301487980907370431155859574851192235000645" + + "6064003773432638837702651724406011257895842835907410397326351149391214249645075873929695397792073094" + + "1188402364506858174852606021099282767046225114299907364917050686481947141812831031312882254660611456" + + "524573907422800164140884130285721050703575"); + + sum = sum.setScale(1000, RoundingMode.HALF_DOWN); + accurateValue = accurateValue.setScale(1000, RoundingMode.HALF_DOWN); + + System.out.println("sin(0.01) up to 1000 decimal places:"); + System.out.println("Calculated Value: " + sum.toPlainString()); + System.out.println("Accurate Value: " + accurateValue.toPlainString()); + System.out.println("Difference: " + accurateValue.subtract(sum).abs().toPlainString()); + } +}