Synchronization in Java

In Java, synchronization is a mechanism that allows multiple threads to access shared resources in a coordinated manner, preventing race conditions and ensuring data consistency. Synchronization is achieved using the synchronized keyword, which can be applied to methods or blocks of code.

Here's an example of using synchronization to ensure thread safety when accessing shared resources:

public class Counter {
  private int count;
  public synchronized void increment() {
    count++;
  }
  public synchronized void decrement() {
    count--;
  }
  public synchronized int getCount() {
    return count;
  }
}
public class Main {
  public static void main(String[] args) {
    Counter counter = new Counter();
    // Create two threads that increment and decrement the counter
    Thread t1 = new Thread(() -> {
      for (int i = 0; i < 100000; i++) {
        counter.increment();
      }
    });
    Thread t2 = new Thread(() -> {
      for (int i = 0; i < 100000; i++) {
        counter.decrement();
      }
    });
    t1.start();
    t2.start();
    // Wait for both threads to complete
    try {
      t1.join();
      t2.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    // Print the final count
    System.out.println("Count: " + counter.getCount());
  }
}

In this example, we define a Counter class with three methods: increment(), decrement(), and getCount(). Each of these methods is marked as synchronized, which means that only one thread can execute them at a time. This ensures that the count variable is accessed in a thread-safe manner.

We then create two threads that increment and decrement the count variable using the increment() and decrement() methods of the Counter class. We start the threads using the start() method and wait for them to complete using the join() method. Finally, we print the final value of the count variable.

Synchronization is an important tool for managing shared resources in multithreaded applications, but it can also introduce performance overhead and can sometimes lead to deadlocks or other issues. It's important to use synchronization appropriately and to ensure that your code is well-designed and thread-safe.