🔥 Mastering Java Concurrency: Dive into Part 1! 🚀
Tags: java, concurrency, devcommunity, multithreading
Published: 2024-04-01 11:44:09

👋 Hello there! Welcome to our Java Concurrency Series! 🚀

🚀 In this series, we'll dive into various aspects of Java concurrency, from basic to advanced topics. We'll begin by tackling fundamental concepts such as **thread caching issues**, **basic thread synchronization**, and utilizing **multiple locks using synchronized code blocks**. Stay tuned for an insightful journey into the world of multithreading!

## Table of Contents
1. [🔄 Understanding Concurrency](#understanding-concurrency)
2. [🧵What are Threads](#what-are-threads)
3. [💾Problem with Thread Caching](#problem-with-thread-caching)
4. [🔒 Solution: Volatile Keyword](#solution-volatile-keyword)
5. [🏎️ Problem: Race Conditions](#problem-race-conditions)
6. [🛠️ Solution: Synchronized Keyword](#solution-synchronized-keyword)
7. [🧱 Synchronize Code Blocks](#synchronize-code-blocks)
8. [🔐 Locking](#locking)

---

## 🔄 Understanding Concurrency
Concurrency is where your computer can handle multiple tasks simultaneously. It involves managing the execution of multiple tasks concurrently to improve performance and responsiveness in software applications.

## 🧵 What are Threads
Thread is like a separate path of execution within a program. When a program is running, it can have multiple threads running **concurrently**, each performing its own set of instructions independently.
## 💾 Problem with Thread Caching

In the code below, we create the `Clock` class as a `Runnable` and run it by spawning a new `Thread` from the main class. Once started, the `Clock` class's run method will keep running the while loop. To stop this, we need to call the `cancel` method from the **main** thread, which will:

1. Change the `isStopped` value to false.
2. Write it in the memory.

However, the thread running the while loop may not have the updated value of `isStopped` and could still have a local cache of the old value of `isStopped` (false). 

```java
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Clock clock  = new Clock();
        Thread thread = new Thread(clock);
        thread.start();
        Thread.sleep(10000);
        clock.cancel();
    }
}

class Clock implements Runnable {
    private volatile boolean isStopped = false;

    public void cancel() {
        this.isStopped = true;
    }

    public void run() {
        while (!this.isStopped) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("Tick");
        }
    }
}
```

## 🔒 Solution: Volatile Keyword 
The values of the `volatile` variable will never be cached, and all writes and reads will be done to and from the main memory. So, the above `thread` running the `while` loop will always have the **latest** read value from the **memory**.

![Volatile Keyword](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qm176e3swf43pjfsatby.png)

## 🏎️ Problem: Race Conditions
Let's consider the scenario where we create the `Counter` class and try to update the `counter` value from **two separate threads**. 
```java
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(counter);
        Thread t2 = new Thread(counter);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("expect to be 2000 but value is : " + counter.getCounter());

    }
}


public class Counter implements Runnable{
    private int counter=0;

    @Override
    public void run() {
        for(int i=0;i<1000;i++){
            increament();
        }
    }
    public void increament(){
        int i =counter;
        counter = i+1;
    }
    public int getCounter(){
        return this.counter;
    }
}

```

can you guess the output? 

```
expect to be 2000 but value is: 1746
```



But why does this happen? Let's understand it:

![Race Condition](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gd5hydiga8794gunpak1.png)

Look at the `increment` method:

1. First, we store the value of the counter to local variable `i`.
2. Then, we increment `i` by 1.
3. Finally, we write `i` back to `counter`.

And two threads are doing this simultaneously.

![Race Condition](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o9os1d5a73uv7vlaswqn.png)

## 🛠️ Solution: Synchronized Keyword
We can make the method synchronized so that only one thread can run the method at a time. Other threads have to wait for their turn to execute the method. This leads to consistent data.

We define a method to be synchronized using the `synchronized` keyword.
```java
public class Counter implements Runnable{
    private int counter=0;

    @Override
    public void run() {
        for(int i=0;i<1000;i++){
            increament();
        }
    }
    public synchronized void increament(){
        int i =counter;
        counter = i+1;
    }
    public int getCounter(){
        return this.counter;
    }
}
```


## 🧱 Synchronize Code Blocks

If two methods are dealing with two different variables, instead of making the entire method synchronized and locking the entire object, we can use synchronized code blocks to lock only the critical sections of each method. This approach allows us to ensure that no two threads can access the same method at the same time, providing better concurrency.


```java
public class ExampleClass {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    private int value1 = 0;
    private int value2 = 0;

    public void incrementValue1() {
        synchronized (lock1) {
            value1++;
            System.out.println("Value 1 incremented to: " + value1);
        }
    }

    public void decrementValue2() {
        synchronized (lock2) {
            value2--;
            System.out.println("Value 2 decremented to: " + value2);
        }
    }
}
```

For instance, imagine we have `incrementValue1()` and `decrementValue2()` methods, each modifying separate variables `value1` and `value2`. We have declared two private final objects lock1 and lock2. These objects are used as locks for synchronized blocks to ensure thread safety when accessing the corresponding variables. By using synchronized code blocks within each method, we can lock only the sections where the shared variables are modified, allowing concurrent access to other methods while maintaining thread safety.





## 🔐 Locking
Locks in concurrency are used to achieve **thread-safety** by enabling synchronous access to shared resources.

#### - Intrinsic Locks
**In Java**, every object has a built-in lock called the **intrinsic lock**. When a thread wants to access a shared object, it must first acquire this lock. This ensures that only one thread can access the object at a time. When you declare that a method as `synchronized`, the thread calling that method acquires the intrinsic lock for that method’s object and releases it when the method exits

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1h8ccte4sv0wqcifgn80.jpg)

For example, consider a `Counter` class where each thread increments a shared counter variable. In this class, the `increment()` method is declared as **synchronized**, which means only one thread can execute it at a time for a given `Counter` object. When a thread calls the `increment()` method, it automatically acquires the intrinsic lock associated with that method's object, ensuring that no other thread can modify the counter concurrently. Once the method completes its execution, the lock is released, allowing other threads to call the method.

---


🌟 Thank you for joining us on this Java Concurrency Series journey! 🚀 We'll delve into **CountdownLatch**, **Callable**, **Futures**, and **Semaphores** soon.

📢 We'd love your feedback to improve our content. Share your thoughts with us!

🤝 **Stay connected, happy coding!** 🌟