Skip to content

Instantly share code, notes, and snippets.

@sb8244
Created September 12, 2023 06:22

Revisions

  1. sb8244 created this gist Sep 12, 2023.
    15 changes: 15 additions & 0 deletions ImmutableCounter.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    public class ImmutableCounter {
    private final int counter;

    public ImmutableCounter(int num) {
    this.counter = num;
    }

    public int getCount() {
    return this.counter;
    }

    public ImmutableCounter increase() {
    return new ImmutableCounter(this.counter + 1);
    }
    }
    84 changes: 84 additions & 0 deletions ImmutableCounterTest.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,84 @@
    import org.junit.jupiter.api.Test;

    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.locks.ReentrantLock;

    import static org.junit.jupiter.api.Assertions.*;

    class ImmutableCounterTest {
    @Test
    void immutableCounterIsImmutable() {
    ImmutableCounter immutableCounter = new ImmutableCounter(0);
    assertEquals(1, immutableCounter.increase().getCount());
    assertEquals(1, immutableCounter.increase().getCount());

    assertEquals(2, immutableCounter.increase().increase().getCount());
    }

    @Test
    void mutableCounterIsNotImmutable() {
    UnsafeCounter counter = new UnsafeCounter(0);
    assertEquals(1, counter.increase());
    assertEquals(2, counter.increase());
    }

    @Test
    void unsafeCounterIsNotThreadSafe() throws InterruptedException {
    UnsafeCounter counter = new UnsafeCounter(0);
    ReentrantLock lock = new ReentrantLock();
    // TODO: How can you use this lock to make the code safe?
    // Hint: It's just like the lock on your home's door
    // Explore: Why locks are difficult to manage in production systems

    Runnable code = () -> {
    for (int i = 0; i < 10000; i++) {
    int startValue = counter.getCount();
    counter.increase();
    int currentValue = counter.getCount();
    assertEquals(startValue + 1, currentValue);
    }
    };

    Thread t1 = new Thread(code);
    Thread t2 = new Thread(code);

    AtomicInteger errorCounter = new AtomicInteger();
    t1.setUncaughtExceptionHandler((Thread t, Throwable e) -> { errorCounter.incrementAndGet(); });
    t2.setUncaughtExceptionHandler((Thread t, Throwable e) -> { errorCounter.incrementAndGet(); });

    t1.start();
    t2.start();

    t1.join();
    t2.join();

    assertEquals(0, errorCounter.get());
    }

    /*
    @Test
    void immutableCounterIsThreadSafe() throws InterruptedException {
    // TODO: We get an error with the current code. How do we get this code to work?
    // Hint: because the counter is immutable we need reassignment.
    ImmutableCounter counter = new ImmutableCounter(0);
    Runnable code = () -> {
    for (int i = 0; i < 10000; i++) {
    int startValue = counter.getCount();
    counter = counter.increase();
    int currentValue = counter.getCount();
    assertEquals(startValue + 1, currentValue);
    }
    };
    Thread t1 = new Thread(code);
    Thread t2 = new Thread(code);
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    }
    */
    }
    16 changes: 16 additions & 0 deletions UnsafeCounter.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,16 @@
    public class UnsafeCounter {
    private int counter;

    public UnsafeCounter(int num) {
    this.counter = num;
    }

    public int increase() {
    this.counter += 1;
    return this.counter;
    }

    public int getCount() {
    return this.counter;
    }
    }