26 August 2023

Synchronization in Java - learngreen.net

 

Synchronization In Java

  By enabling many activities to run concurrently, concurrent programming is essential in the realm of software development for performance optimization. The problem of guaranteeing that several threads can access shared resources without introducing data corruption or unexpected behavior, however, arises with concurrency. Synchronization is useful in this situation. A key feature of Java is synchronization, which gives programmers control over how many threads can use the same resources at once while maintaining data integrity and avoiding race events.


Multiple threads may access and alter shared resources concurrently without sufficient synchronization, producing inconsistent or inaccurate results.


The Importance of Synchronization:-

Consider a situation where several threads are attempting to increase a common counter variable. Without synchronization, two threads might concurrently read the value of the counter, independently increment it, and then write back the results. Due to this, the counter might not increase by the anticipated amount, which could result in incorrect computations or unexpected behavior.


Synchronization Mechanisms in Java:-

Synchronized methods and synchronized blocks are the two primary synchronization mechanisms offered by Java.


Methods that can only be accessed by one thread at a time are referred to as synchronized methods. The monitor of an object is locked when a thread enters a synchronized method, preventing other threads from entering any additional synchronized methods on the same object. This guarantees that only one thread at a time can use the synchronized procedure.


Blocks that are synchronized: Blocks that are synchronized provide you with more precise control over synchronization. You can synchronize particular code sections as opposed to the full function. When you want to avoid locking the entire process to improve performance, this is quite helpful.


One thread can only execute the enclosed code block at a time in a critical section created by a synchronized block in Java. When numerous threads are concurrently gaining access to common resources, this is helpful for guaranteeing thread safety. The following is the fundamental grammar for writing a synchronized block



   synchronized (object) {
      // Code that needs to be executed in a synchronized manner
    }

 Synchronized blocks are defined with the term synchronized.


(object): Specify the object to which the synchronization will be applied inside the parenthesis. This item is frequently referred to as the "lock" or "monitor" object. It is employed to organize the synchronization of various threads.


You insert the code that you wish to run in synchronization across processes inside the curly braces. Based on the lock provided by the specified object, only one thread will be allowed to execute this block at once.


Compared to conventional synchronized blocks, locks in Java offer a more flexible and fine-grained method of managing synchronization. The java.util.concurrent.locks package contains locks, which provide more sophisticated functionality and synchronization control. Java offers the ReentrantLock and the ReadWriteLock as its two primary lock types.


ReentrantLock:-  A more potent substitute for synchronized blocks is the ReentrantLock. It offers more advanced synchronization features, supports interruptible and timed lock acquisition, and gives you more control over locking and unlocking. 


One thread may write to a resource at a time but multiple threads may read it simultaneously thanks to the ReadWriteLock synchronization feature. In situations where reads occur more frequently than writes, this might be more effective. The actual ReadWriteLock implementation is called ReentrantReadWriteLock.


The following are the key benefits of utilizing locks, especially the ReentrantLock and ReadWriteLock:-

power over locking and unlocking on a finer scale.

Support for conditions enables threads to wait until particular criteria are satisfied before continuing.

flexibility in the timing and acquisition of interruptible locks.

With ReadWriteLock, read-intensive scenarios perform better.

checking the lock's state, which is helpful for monitoring and troubleshooting.

But increasing responsibility also comes along with this increased authority and adaptability. Lock management may become increasingly difficult, and improper use may result in deadlocks or other synchronization problems. To prevent potential issues while utilizing locks, it's crucial to adhere to standard practices and ensure correct exception handling and cleaning.


Remember that Java also provides other synchronization mechanisms and concurrent data structures like Semaphore, CountDownLatch, CyclicBarrier, and more, each tailored for specific synchronization needs. The choice of synchronization mechanism depends on the specific requirements of your application.


The synchronized keyword in Java can be used to define synchronized methods. Only one thread can operate on a single instance of the class at a time when a method is designated as synchronized. When many threads are concurrently accessing the methods of the same object, this helps to ensure thread safety.



public synchronized void methodName( ) {
    // Synchronized method code
}

 Due to race circumstances and concurrent access concerns, when multiple threads access shared resources or data concurrently without synchronization, it can result in a variety of errors and unexpected behaviors. When the time or order of thread execution affects how anything turns out, a race condition has occurred. The following are some typical problems that can develop without synchronization:


Data corruption can occur when multiple threads read and modify shared data at the same time. For instance, if one thread reads the data while another is modifying it, the results could be inconsistent or incorrect.


Consistent State:- It's possible for threads to interact with shared objects without having adequate synchronization, in which case they might not be aware of any alterations performed by other threads. This may cause things to behave inconsistently or unexpectedly.


Updates made by one thread may be lost if they are overwritten by another before being transferred to memory in the absence of synchronization.


Deadlocks:- When two or more threads are unable to move forward because one is awaiting a resource that the other thread is holding, a deadlock has occurred. When threads acquire locks in a different order, this can occur.


Livelocks:- When two or more threads continuously modify their states in reaction to modifications in the states of the other threads, no progress is made.


Performance Problems:- Thread contention for locks can occasionally result in performance bottlenecks and decreased parallelism.



  class MRBL implements Runnable {
   // Implement the run method required by Runnable interface
    public synchronized void run() {
    for (int i = 1; i <= 3; i++) {
      // Print thread ID and loop index
    System.out.println("Thread " + Thread.currentThread().getId() + " " + i);
          }
      }
     }
    public class SynchronizedThreadExample {
    public static void main(String[] args) {
      // Create an instance of the MRBL class
     MRBL obj = new MRBL( );
    // Create two threads, both sharing the same MRBL instance
        Thread thread1 = new Thread(obj);
        Thread thread2 = new Thread(obj);
   // Start both threads
        thread1.start( );
        thread2.start( );
    try {
    // Wait for both threads to complete
     thread1.join( );
     thread2.join( );
     } catch (InterruptedException e) {
     e.printStackTrace( );
            }
        }
     }
 

 


     class CT {
     public synchronized void debug() { // Synchronized method
        for (int i = 0; i <= 3; i++) {
            System.out.println("CT " + Thread.currentThread().getId() + " " + i);
          }
        }
    }

    class MT extends CT {
    public synchronized void debug() { // Synchronized method
        for (int i = 0; i <= 3; i++) {
            System.out.println("MT " + Thread.currentThread().getId() + " " + i);
          }
      }
  }

    public class SynchronizedThreadDemo {
    public static void main(String[ ] args) {
        MT obj = new MT( );
        MT obj1 = new MT( );

       // Create and start the first thread
        Thread thread1 = new Thread(() -> {
            obj.debug(); // Call the synchronized debug method of MT class for thread1
        });

        // Create and start the second thread
        Thread thread2 = new Thread(() -> {
            obj1.debug(); // Call the synchronized debug method of MT class for thread2
        } );

        // Start both threads
        thread1.start();
        thread2.start();
      }
   }

 

No comments:

Post a Comment