Synchronization. Monitor. General concepts. The synchronized keyword
Contents
- 1. The concept of synchronization between threads. The need to apply synchronization. Monitor
- 2. The synchronized access modifier. General form
- 3. The synchronized() { } operator. General form
- 4. An example demonstrating synchronized access to a shared method from three different threads. Applying the synchronized access modifier
- 5. An example of using the synchronized () {} operator for synchronized access to a shared resource
- Related topics
Search other resources:
1. The concept of synchronization between threads. The need to apply synchronization. Monitor
There are times when two or more parallel-executing threads are trying to access a shared resource. If the resource can be changed as a result of the execution of one of the threads, then the other threads must wait until the changes in the thread are completed. Otherwise, threads will receive a resource whose data will be erroneous.
Synchronization can ensure that only one thread uses a shared resource at a time. This means that synchronization is a process that orders access from different threads to a shared resource. Monitor is an object that is used for mutually exclusive locking.
Mutual locking allows only one thread object to own the monitor. Each thread object has its own, implicitly associated monitor.
The thread of execution (which is represented by the object) can take over the monitor if it has requested a lock and the monitor is free at the moment. After an object has entered the monitor, all other thread objects attempting to enter the monitor are suspended and wait until the first object exits the monitor. Only one thread can have a monitor. If the stream (object) has a monitor, then it can re-enter it if necessary.
In the Java language, synchronization is applied to entire methods or pieces of code.
Based on this, there are two ways to synchronize the program code:
- by using the synchronized access modifier;
- by using the synchronized () {} operator.
⇑
2. The synchronized access modifier. General form
The synchronized access modifier is applied when a synchronized method is declared and has the following general form:
synchronized return_type MethodName(parameters) {
...
}
here
- MethodName is the name of the method that is a shared resource and needs to be synchronized;
- return_type – the type that the method returns;
- parameters – method parameters.
⇑
3. The synchronized() { } operator. General form
The synchronized() operator, unlike the synchronized access modifier, is used to apply synchronization to objects (methods) that were not originally designed for multithreading access. That is, the class does not implement methods with the synchronized access modifier (third-party classes).
The general form of the synchronized () statement is as follows:
synchronized (reference) { // Operators to be synchronized // ... }
here
- reference – a reference to the object being synchronized;
- curly braces {} that define a sync block. This block specifies the operators for synchronization.
⇑
4. An example demonstrating synchronized access to a shared method from three different threads. Applying the synchronized access modifier
The example demonstrates the need to use the synchronized access modifier in order to order resource access from different threads.
The shared resource is the Get() method of the Array5 class. The Get() method returns an array of 5 numbers, whose values change sequentially from 1 to 5. This method will be attempted to be accessed simultaneously from different threads, so the method is designated as synchronized.
The encapsulation of a single thread of execution is carried out in the ArrayThread class in the classical (standard) way using the implementation of the Runnable interface. The ArrayThread class contains the following public members:
- A is a reference to the instance of the Array5 class. This reference is used to access the Get() method;
- AI – an array of numbers obtained from the Get() method;
- t – a reference to the current thread;
- constructor ArrayThread(), which receives an instance of the Array5 class and the name of the thread. In the constructor, a thread of execution is created and started;
- the run() method, which calls the Get() method using reference A.
The Threads class provides a main() function that creates three threads AT1, AT2, AT3 of type ArrayThread and one object A5.
// Topic. Synchronization of streams. // The class containing the shared resource class Array5 { // A synchronized method that returns an array of 5 numbers // and prints it to the screen synchronized public int[] Get(String threadName) { // Print the name of the running thread System.out.println("Begin => " + threadName); // Form 5 integers from 1 to 5 int[] AI = new int[5]; for (int i=0; i<AI.length; i++) AI[i] = i+1; // Cause a delay, so that other threads can be executed try { Thread.sleep(100); } catch (InterruptedException e) { System.out.println(e.getMessage()); } // Output the AI array in the method for control System.out.print(threadName + " = [ "); for (int i=0; i<AI.length; i++) System.out.print(AI[i] + " "); System.out.println(" ]"); // Notify about the end of method execution System.out.println("End => " + threadName); return AI; } } // A class that is a thread that reads an array of numbers class ArrayThread implements Runnable { Array5 A; // the instance of a shared resource int[] AI; // an array of 5 numbers that is obtained from a shared resource Thread t; // a reference to the current thread // Constructor - receives parameters: // - stream name; // - an instance of the Array5 class. ArrayThread(String threadName, Array5 A) { // Save array this.A = A; // Create a thread t = new Thread(this, threadName); // Start the thread for execution t.start(); // invoke the run() method } // Method in which a thread is started that creates an array of 5 numbers public void run() { AI = A.Get(t.getName()); // get an array of numbers from 1 to 5 } } public class Threads { public static void main(String[] args) { // Demonstrate access to a shared resource from 3 different threads // 1. Declare an instance to be a shared resource on three different threads Array5 A5 = new Array5(); // 2. Declare three different threads and run them ArrayThread AT1 = new ArrayThread("AT1", A5); ArrayThread AT2 = new ArrayThread("AT2", A5); ArrayThread AT3 = new ArrayThread("AT3", A5); // 3. Wait for all three threads to complete try { AT1.t.join(); AT2.t.join(); AT3.t.join(); } catch (InterruptedException e) { System.out.println(e.getMessage()); } } }
The result of the program
Begin => AT1 AT1 = [ 1 2 3 4 5 ] End => AT1 Begin => AT3 AT3 = [ 1 2 3 4 5 ] End => AT3 Begin => AT2 AT2 = [ 1 2 3 4 5 ] End => AT2
If in the above example, before the Get() method of the Array5 class, remove the synchronized keyword
class Array5 { ... // there is no synchronized modifier here public int[] Get(String threadName) { ... } }
then there will be no sequential execution of threads. In this case, after each launch, the program will produce a different (chaotic) result, for example, the following
Begin => AT1 Begin => AT2 Begin => AT3 AT1 = [ AT2 = [ 1 1 2 2 3 3 4 4 5 ] End => AT1 5 ] End => AT2 AT3 = [ 1 2 3 4 5 ] End => AT3
⇑
5. An example of using the synchronized () {} operator for synchronized access to a shared resource
The code of the previous example (see section 4), synchronizing threads, can be represented in a different way. In this case, instead of the synchronized access modifier, use the synchronized() {} statement before the Get() method name. The synchronized statement must be used in the run() method of the ArrayThread class. After the changes made, the abbreviated program code will be as follows:
// A class that is a shared resource class Array5 { // A method that returns an array of 5 numbers and displays it on the screen. // It is not synchronized already public int[] Get(String threadName) { ... } } // A class that is a thread reading an array of numbers class ArrayThread implements Runnable { Array5 A; // the instance of shared resource int[] AI; // an array of 5 numbers from a shared resource Thread t; // a reference to the current thread // Constructor - receives parameters: // - thread name; // - the instance of Array5 class. ArrayThread(String threadName, Array5 A) { ... } // Method in which a thread is started that creates an array of 5 numbers public void run() { // Operator synchronized to the shared resource A synchronized (A) { AI = A.Get(t.getName()); // get an array of numbers from 1 to 5 } } }
⇑
Related topics
- Multitasking. Threads of execution. Basic concepts
- Java language tools for working with threads of execution. Thread class. Runnable interface. Main thread of execution. Creating a child thread
- Methods of the Thread class: getName(), start(), run(), sleep(). Examples
- Methods of the Thread class: join(), isAlive(), getPriority(), setPriority(). Examples
- Interaction between threads. Methods wait(), notify(), notifyAll(). Examples
- The state of the thread of execution. The getState() method. Example
⇑