Java 9 different solutions: Exchanger, Barrier, Latch, Lock etc
Still learning. There are just so many choices Java gave us to handle multithreading.\n\nI am sure there are more ways to do this. And my knowledge is limited, 
Still learning. There are just so many choices Java gave us to handle multithreading.\n\nI am sure there are more ways to do this. And my knowledge is limited, I will come back to this post and improve and add more comparison once I understand better.\n\nFor synchronized solution:\n\nIf a thread calls wait() but no other threads are performing a corresponding notify() or notifyAll(), those threads will remain in the waiting state indefinitely. Moreover, if your application creates an excessive number of threads that all try to acquire the lock using synchronized blocks, it could lead to resource exhaustion or decreased performance due to excessive context switching.\n\nWe need to put wait() in a loop, also check the condition in the loop. Java does not guarantee that the thread will be woken up only by a notify()/notifyAll() call or the right notify()/notifyAll() call at all. Because of this property the loop-less version might work on your development environment and fail on the production environment unexpectedly.\n\nA thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one:\n\n     synchronized (obj) {\n         while (<condition does not hold>)\n             obj.wait(timeout);\n         ... // Perform action appropriate to condition\n     }\nhttps://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#wait(long)\n\n### volatile\nvolatile keyword id quite different from other mechanism. It\'s to tell JVM to put the variable in the main memory of the server, instead of the cache on the cpu. When we have multi threads running on the server, they run on diferent CPUs, then using their own caches, so one CPU may get older value when another CPU already updated the value in its cache. Volatile keyword ensures that any thread reading the variable sees the most recent modification made by any other thread.\n\nWe should understand that this is on web server level, not database level. A often brought up question during interviews is when we have multi web servers trying to update the same data in database, how do we make sure we are getting the latest update. Volatile will have nothing to do with that.\n\nAnother question is about redis, is there a similar mechanism in Redis? answer is no, since redis is single threaded by default, that\'show it\'s built. How to sync up redis servers in different data centers is a totally different challenge.\n```\n    public static volatile int methodCompleted;\n    public Foo() {\n        methodCompleted = 0;\n    }\n\n    public void first(Runnable printFirst) throws InterruptedException {\n        // printFirst.run() outputs "first". Do not change or remove this line.\n        printFirst.run();\n        methodCompleted = 1;\n    }\n\n    public void second(Runnable printSecond) throws InterruptedException {\n        while(methodCompleted != 1) ;\n        // printSecond.run() outputs "second". Do not change or remove this line.\n        printSecond.run();\n        methodCompleted = 2;\n    }\n\n    public void third(Runnable printThird) throws InterruptedException {\n        while(methodCompleted != 2) ;\n        // printThird.run() outputs "third". Do not change or remove this line.\n        printThird.run();\n        methodCompleted = 3;\n    }\n```\n\n### Exchanger\n\n```\nimport java.util.concurrent.Exchanger;\n\npublic class Foo {\n    private final Exchanger<Boolean> exchanger12 = new Exchanger<>();\n    private final Exchanger<Boolean> exchanger23 = new Exchanger<>();\n\n    public Foo() {}\n\n    public void first(Runnable printFirst) throws InterruptedException {\n        printFirst.run();\n        try {\n            exchanger12.exchange(true); // Signal that first() has completed\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n        }\n    }\n\n    public void second(Runnable printSecond) throws InterruptedException {\n        try {\n            exchanger12.exchange(true); // Wait for first() to complete\n            printSecond.run();\n            exchanger23.exchange(true); // Signal that second() has completed\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n        }\n    }\n\n    public void third(Runnable printThird) throws InterruptedException {\n        try {\n            exchanger23.exchange(true); // Wait for second() to complete\n            printThird.run();\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n        }\n    }\n}\n\n```\n### CyclicBarrier\n```\nimport java.util.concurrent.BrokenBarrierException;\nimport java.util.concurrent.CyclicBarrier;\n\npublic class Foo {\n    private final CyclicBarrier barrier1 = new CyclicBarrier(2);\n    private final CyclicBarrier barrier2 = new CyclicBarrier(2);\n\n    public Foo() {}\n\n    public void first(Runnable printFirst) throws InterruptedException {\n        printFirst.run();\n        try {\n            barrier1.await();\n        } catch (BrokenBarrierException e) {}\n\n    }\n\n    public void second(Runnable printSecond) throws InterruptedException {\n        try {\n            barrier1.await();\n        } catch (BrokenBarrierException e) {}\n        printSecond.run();\n        try {\n            barrier2.await();\n        } catch (BrokenBarrierException e) {}\n    }\n\n    public void third(Runnable printThird) throws InterruptedException {\n        try {\n            barrier2.await();\n        } catch (BrokenBarrierException e) {}\n        printThird.run();\n    }\n}\n```\n\n### Latch:\n```\nimport java.util.concurrent.CountDownLatch;\n\npublic class Foo {\n    private CountDownLatch latch1;\n    private CountDownLatch latch2;\n\n    public Foo() {\n        latch1 = new CountDownLatch(1);\n        latch2 = new CountDownLatch(1);\n    }\n\n    public void first(Runnable printFirst) throws InterruptedException {\n        // printFirst.run() outputs "first". Do not change or remove this line.\n        printFirst.run();\n        latch1.countDown(); // Signal that the first method has been executed\n    }\n\n    public void second(Runnable printSecond) throws InterruptedException {\n        latch1.await(); // Wait until the first method completes\n        // printSecond.run() outputs "second". Do not change or remove this line.\n        printSecond.run();\n        latch2.countDown(); // Signal that the second method has been executed\n    }\n\n    public void third(Runnable printThird) throws InterruptedException {\n        latch2.await(); // Wait until the second method completes\n        // printThird.run() outputs "third". Do not change or remove this line.\n        printThird.run();\n    }\n}\n```\n\n### Lock (same as Mutex):\n```\nclass Foo {\n    private volatile int jobCompleted = 0;\n    private final Lock lock = new ReentrantLock();\n    private final Condition isJobCompleted = lock.newCondition();\n\n    private void orderJob(final int jobNumber, Runnable job) throws InterruptedException {\n        lock.lock();\n        try {\n            while (jobCompleted != jobNumber - 1) isJobCompleted.await();\n            job.run();\n            jobCompleted = jobNumber;\n            isJobCompleted.signalAll();\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public void first(Runnable printFirst) throws InterruptedException {\n        orderJob(1, printFirst);\n    }\n\n    public void second(Runnable printSecond) throws InterruptedException {\n        orderJob(2, printSecond);\n    }\n\n    public void third(Runnable printThird) throws InterruptedException {\n        orderJob(3, printThird);\n    }\n}\n```\n\n### AtomicBoolean:\n```\nclass Foo {\n\n  private AtomicBoolean firstJobDone = new AtomicBoolean(false);\n  private AtomicBoolean secondJobDone = new AtomicBoolean(false);\n\n  public Foo() {}\n\n  public void first(Runnable printFirst) throws InterruptedException {\n    // printFirst.run() outputs "first".\n    printFirst.run();\n    // mark the first job as done, by increasing its count.\n    firstJobDone.set(true);\n  }\n\n/*Second and third threads in Java solution based on AtomicInteger will consume CPU cycles while waiting. This may be good to have faster runtime in the test, but in real life it is better to have these threads in WAITING state. For concurrency tasks it makes sense to add one more index - CPU load or something like this (in addition to runtime and memory distributions)\n*/\n  public void second(Runnable printSecond) throws InterruptedException {\n    while (!firstJobDone.get()) {\n      // waiting for the first job to be done.\n    }\n    // printSecond.run() outputs "second".\n    printSecond.run();\n    // mark the second as done, by increasing its count.\n    secondJobDone.set(true);\n  }\n\n  public void third(Runnable printThird) throws InterruptedException {\n    while (!secondJobDone.get()) {\n      // waiting for the second job to be done.\n    }\n    // printThird.run() outputs "third".\n    printThird.run();\n  }\n}\n```\n### AtomicBooleans Approach:\n\nAtomicBooleans provide a simple mechanism for ensuring visibility and atomicity in thread-safe operations without explicit synchronization.\nThe AtomicBoolean approach ensures that threads don\'t waste CPU cycles actively waiting for conditions to be met.\nHowever, the while loop continuously polls for the state change, potentially consuming CPU resources.\n\nEverybody has seen these three:\n\n### Semaphore:\n```\nclass Foo {\n    // By starting with 0 permits, you ensure that any attempt to acquire a permit \n    // using s1.acquire() in the second method will block until s1.release() is called \n    Semaphore s1 = new Semaphore(0), s2 = new Semaphore(0);\n    public Foo() {\n        \n    }\n\n    public void first(Runnable printFirst) throws InterruptedException {\n        \n        // printFirst.run() outputs "first". Do not change or remove this line.\n        printFirst.run();\n        //  increases the number of available permits by 1.\n        s1.release();\n    }\n\n    public void second(Runnable printSecond) throws InterruptedException {\n        s1.acquire();\n        // printSecond.run() outputs "second". Do not change or remove this line.\n        printSecond.run();\n        s2.release();\n    }\n\n    public void third(Runnable printThird) throws InterruptedException {\n        s2.acquire();\n        // printThird.run() outputs "third". Do not change or remove this line.\n        printThird.run();\n    }\n}\n\n```\n\n\n### Semaphore Approach:\n\nSemaphores are synchronization constructs used to control access to a shared resource.\nThis approach allows threads to block until a signal (in the form of a release) is received, minimizing active CPU polling.\nSemaphores provide a more efficient solution in terms of not actively consuming CPU resources while waiting for conditions to be met.\n\n### AtomicInteger:\n```\nclass Foo {\n\n  private AtomicInteger firstJobDone = new AtomicInteger(0);\n  private AtomicInteger secondJobDone = new AtomicInteger(0);\n\n  public Foo() {}\n\n  public void first(Runnable printFirst) throws InterruptedException {\n    // printFirst.run() outputs "first".\n    printFirst.run();\n    // mark the first job as done, by increasing its count.\n    firstJobDone.incrementAndGet();\n  }\n\n  public void second(Runnable printSecond) throws InterruptedException {\n    while (firstJobDone.get() != 1) {\n      // waiting for the first job to be done.\n    }\n    // printSecond.run() outputs "second".\n    printSecond.run();\n    // mark the second as done, by increasing its count.\n    secondJobDone.incrementAndGet();\n  }\n\n  public void third(Runnable printThird) throws InterruptedException {\n    while (secondJobDone.get() != 1) {\n      // waiting for the second job to be done.\n    }\n    // printThird.run() outputs "third".\n    printThird.run();\n  }\n}\n```\n\nAtomicInteger should be similar as AtomicBoolean, just not as clean for this question.\n\n### synchronized:\n```\nclass Foo {\n    private boolean oneDone;\n    private boolean twoDone;\n    \n    public Foo() {\n        oneDone = false;\n        twoDone = false;\n    }\n\n    public synchronized void first(Runnable printFirst) throws InterruptedException {\n        \n        // printFirst.run() outputs "first". Do not change or remove this line.\n        printFirst.run();\n        oneDone = true;\n        notifyAll();\n    }\n\n    public synchronized void second(Runnable printSecond) throws InterruptedException {\n        while(!oneDone) wait();\n        // printSecond.run() outputs "second". Do not change or remove this line.\n        printSecond.run();\n        twoDone = true;\n        notifyAll();\n    }\n\n    public synchronized void third(Runnable printThird) throws InterruptedException {\n        while(!twoDone) wait();\n        // printThird.run() outputs "third". Do not change or remove this line.\n        printThird.run();\n    }\n}\n```\n\nI put the discussion part of this approach at top of the article, since this is the most popular solution, and basic for us to start from.
['Java']