diff --git a/.idea/misc.xml b/.idea/misc.xml
index 5cd9a10..12591c7 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,7 +1,7 @@
-
+
\ No newline at end of file
diff --git a/Report.md b/Report.md
new file mode 100644
index 0000000..0c98dca
--- /dev/null
+++ b/Report.md
@@ -0,0 +1,126 @@
+# Atomic Variables
+
+```java
+import java.util.concurrent.atomic.AtomicInteger;
+public class AtomicDemo {
+ private static AtomicInteger atomicCounter = new AtomicInteger(0);
+ private static int normalCounter = 0;
+ public static void main(String[] args) throws InterruptedException {
+ Runnable task = () -> {
+ for (int i = 0; i < 1_000_000; i++) {
+ atomicCounter.incrementAndGet();
+ normalCounter++;
+ }
+ };
+
+ Thread t1 = new Thread(task);
+ Thread t2 = new Thread(task);
+ t1.start();
+ t2.start();
+ t1.join();
+ t2.join();
+
+ System.out.println("Atomic Counter: " + atomicCounter);
+ System.out.println("Normal Counter: " + normalCounter);
+ }
+}
+
+```
+
+## **Questions:**
+
+- What output do you get from the program? Why?
+
+- What is the purpose of AtomicInteger in this code?
+
+- What thread-safety guarantees does atomicCounter.incrementAndGet() provide?
+
+- In which situations would using a lock be a better choice than an atomic variable?
+
+- Besides AtomicInteger, what other data types are available in the java.util.concurrent.atomic package?
+
+---
+
+## **Answers** :
+
+### Q1 :
+
+- Atomic Counter: 2000000
+- Normal Counter: Less than 2000000
+
+#### Why:
+
+- `atomicCounter` uses `AtomicInteger`, which ensures atomic increments, resulting in exactly 2,000,000 (1M increments per thread).
+
+- `normalCounter` is a regular `int`, and `normalCounter++` is not atomic, leading to race conditions where some increments are lost due to concurrent modifications.
+
+### Q2 :
+
+`AtomicInteger` provides thread-safe, atomic operations to safely increment `atomicCounter` without locks, preventing race conditions in a multithreaded environment.
+
+### Q3 :
+
+`incrementAndGet()` is atomic, ensuring that the read, increment, and write operations are performed as a single, indivisible unit.
+
+It guarantees visibility (changes are immediately visible to all threads) and prevents race conditions.
+
+### Q4 :
+
+- When operations involve multiple variables or complex logic that require mutual exclusion (e.g., updating two counters consistently).
+- When fairness or explicit control over locking (e.g., ReentrantLock) is needed.
+
+### Q5 :
+
+#### Other Data Types:
+
+- `AtomicLong`: For atomic operations on `long` values.
+- `AtomicBoolean`: For atomic operations on `boolean` values.
+- `AtomicReference`: For atomic operations on object references.
+- `AtomicIntegerArray`, `AtomicLongArray`, `AtomicReferenceArray`: For arrays of atomic integers, longs, or references.
+
+---
+
+# Monte Carlo π Estimation Report
+
+## Performance Comparison
+
+- **Single-Threaded Version:**
+ - Execution Time: ~1.4-2 seconds for 50,000,000 points.
+ - Estimated π: ~3.1416 (accuracy depends on point count).
+
+- **Multi-Threaded Version (4 threads):**
+ - Execution Time: ~0.1-0.3 second for 50,000,000 points.
+ - Estimated π: ~3.1416 (same accuracy as single-threaded).
+
+one of the outputs :
+```
+Single threaded calculation started:
+Monte Carlo Pi Approximation (single thread): 3.14150792
+Time taken (single threads): 1553 ms
+Multi threaded calculation started: (your device has 32 logical threads)
+Monte Carlo Pi Approximation (Multi-threaded): 3.14165904
+Time taken (Multi-threaded): 161 ms
+```
+## Questions
+
+### Was the multi-threaded implementation always faster than the single-threaded one?
+
+No, the multi-threaded implementation is not always faster.
+
+**Why not?**
+- Small point counts (e.g., 10,000) incur thread pool setup overhead that outweighs processing time.
+- Excessive threads beyond CPU cores (e.g., 16 threads on 4 cores) cause context switching overhead.
+- Single-core systems lack parallelism, making multi-threading slower.
+- Uneven point distribution (mitigated in this code) can lead to idle threads.
+
+### If not, what factors are the cause and what can you do to mitigate these issues?
+
+**Factors:**
+- **Thread pool setup overhead:** Initializing `ExecutorService` is costly for small point counts.
+- **Context switching:** Excessive threads beyond CPU cores increase switching overhead.
+- **Hardware limitations:** Single-core systems lack parallelism.
+
+**Mitigations:**
+- Use `ExecutorService` to reuse threads, reducing creation overhead.
+- Set thread count to match CPU cores (`availableProcessors()`).
+- Fall back to single-threaded version for small point counts (<1M).
\ No newline at end of file
diff --git a/src/main/java/Banking/BankAccount.java b/src/main/java/Banking/BankAccount.java
index 801bbfa..1844d73 100644
--- a/src/main/java/Banking/BankAccount.java
+++ b/src/main/java/Banking/BankAccount.java
@@ -17,8 +17,7 @@ public int getId(){
return id;
}
public int getBalance() {
- // TODO: Consider locking (if needed)
- return balance;
+ return balance; // No need for a lock, as access to the balance is safe in other methods
}
public Lock getLock() {
@@ -26,16 +25,40 @@ public Lock getLock() {
}
public void deposit(int amount) {
- // TODO: Safely add to balance.
+ lock.lock();
+ try {
+ balance += amount;
+ } finally {
+ lock.unlock();
+ }
}
public void withdraw(int amount) {
- // TODO: Safely withdraw from balance.
+ lock.lock();
+ try {
+ balance -= amount;
+ } finally {
+ lock.unlock();
+ }
}
public void transfer(BankAccount target, int amount) {
- // TODO: Safely make the changes
- // HINT: Both accounts need to be locked, while the changes are being made
- // HINT: Be cautious of potential deadlocks.
+ // Locking order by id to avoid deadlock
+ Lock firstLock = this.id < target.id ? this.lock : target.lock;
+ Lock secondLock = this.id < target.id ? target.lock : this.lock;
+
+ firstLock.lock();
+ try {
+ secondLock.lock();
+ try {
+ this.balance -= amount; // Deduction from the first account
+ target.balance += amount; // Deposit to destination account
+ } finally {
+ secondLock.unlock();
+ }
+ } finally {
+ firstLock.unlock();
+ }
}
+
}
diff --git a/src/main/java/Banking/BankingMain.java b/src/main/java/Banking/BankingMain.java
index 0e52f8f..095f09c 100644
--- a/src/main/java/Banking/BankingMain.java
+++ b/src/main/java/Banking/BankingMain.java
@@ -15,7 +15,7 @@ public List calculate() throws InterruptedException {
Thread[] threads = new Thread[4];
for(int i = 1; i <= 4; i++){
String fileName = i + ".txt";
- threads[i - 1] = new Thread(new TransactionProcessor(accounts.get(i - 1), fileName, accounts));
+ threads[i - 1] = new Thread(new TransactionProcessor(accounts.get(i - 1), fileName, accounts) , ("Acc-" + i));
}
for(Thread thread : threads){
diff --git a/src/main/java/MonteCarloPI/MonteCarloPi.java b/src/main/java/MonteCarloPI/MonteCarloPi.java
index 861fe73..265e902 100644
--- a/src/main/java/MonteCarloPI/MonteCarloPi.java
+++ b/src/main/java/MonteCarloPI/MonteCarloPi.java
@@ -1,8 +1,10 @@
package MonteCarloPI;
-import java.util.concurrent.ExecutionException;
+import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicLong;
public class MonteCarloPi {
@@ -25,31 +27,73 @@ public static void main(String[] args) throws InterruptedException, ExecutionExc
endTime = System.nanoTime();
System.out.println("Monte Carlo Pi Approximation (Multi-threaded): " + piWithThreads);
System.out.println("Time taken (Multi-threaded): " + (endTime - startTime) / 1_000_000 + " ms");
-
- // TODO: After completing the implementation, reflect on the questions in the description of this task in the README file
- // and include your answers in your report file.
}
// Monte Carlo Pi Approximation without threads
public static double estimatePiWithoutThreads(long numPoints)
{
- // TODO: Implement this method to calculate Pi using a single thread
- return 0;
+ Random random = new Random();
+ long pointsInsideCircle = 0;
+
+ for (long i = 0; i < numPoints; i++) {
+ double x = random.nextDouble() * 2 - 1; // x in [-1, 1]
+ double y = random.nextDouble() * 2 - 1; // y in [-1, 1]
+ if (x * x + y * y <= 1) { // Inside circle
+ pointsInsideCircle++;
+ }
+ }
+
+ return 4.0 * pointsInsideCircle / numPoints;
}
// Monte Carlo Pi Approximation with threads
- public static double estimatePiWithThreads(long numPoints, int numThreads) throws InterruptedException, ExecutionException
- {
- // TODO: Implement this method to calculate Pi using multiple threads
+ public static double estimatePiWithThreads(long numPoints, int numThreads) throws InterruptedException {
+ AtomicLong pointsInsideCircle = new AtomicLong(0);
+ Thread[] threads = new Thread[numThreads];
+ long pointsPerThread = numPoints / numThreads;
+ long remainingPoints = numPoints % numThreads;
+
+ // Distribute points among threads using try-with-resources
+ try (ExecutorService executor = Executors.newFixedThreadPool(numThreads)) {
+ for (int i = 0; i < numThreads; i++) {
+ long pointsForThisThread = pointsPerThread + (i == 0 ? remainingPoints : 0);
+ threads[i] = new Thread(new PiTask(pointsForThisThread, pointsInsideCircle));
+ executor.execute(threads[i]); // Use executor to manage threads
+ }
+
+ // Wait for all threads to complete
+ for (Thread thread : threads) {
+ thread.join();
+ }
+ } // ExecutorService automatically shuts down here
+
+ return 4.0 * pointsInsideCircle.get() / numPoints;
+ }
+
+ // Task for each thread
+ private static class PiTask implements Runnable {
+ private final long numPoints;
+ private final AtomicLong pointsInsideCircle;
+
+ public PiTask(long numPoints, AtomicLong pointsInsideCircle) {
+ this.numPoints = numPoints;
+ this.pointsInsideCircle = pointsInsideCircle;
+ }
- ExecutorService executor = Executors.newFixedThreadPool(numThreads);
+ @Override
+ public void run() {
+ Random random = new Random();
+ long localCount = 0;
- // HINT: You may need to create a variable to *safely* keep track of points that fall inside the circle
- // HINT: Each thread should generate and process a subset of the total points
+ for (long i = 0; i < numPoints; i++) {
+ double x = random.nextDouble() * 2 - 1;
+ double y = random.nextDouble() * 2 - 1;
+ if (x * x + y * y <= 1) {
+ localCount++;
+ }
+ }
- // TODO: After submitting all tasks, shut down the executor to prevent new tasks
- // TODO: wait for the executor to be fully terminated
- // TODO: Calculate and return the final estimation of Pi
- return 0;
+ pointsInsideCircle.addAndGet(localCount);
+ }
}
}
\ No newline at end of file