The most basic synchronization method of Java is to use the synchronized keyword to control the concurrent access of a method. Every method declared with the synchronized keyword is a critical section. In Java, only one of the critical sections of the same object is allowed to be accessed at the same time.
Static methods have different behaviors. A static method declared with the synchronized keyword can only be accessed by one execution thread, but other threads can access the non-static synchronized method of this object. This must be very cautious because two threads can access two different synchronized methods of an object at the same time, i.e. one of which is a static synchronized method and the other is a non-static synchronized method. If both methods change the same data, an inconsistent error will occur.
The syntax of the synchronized block is as follows:
public void method() { synchronized(expression) { } } There are two uses of the synchronized keyword. One is only used in the definition of methods, and the other is the synchronized block. We can not only use synchronized to synchronize an object variable, but you can also use synchronizedl to synchronize static methods and non-static methods in the class.
The first: Synchronization of non-static methods
From the Java related syntax, we can know that using the synchronized keyword to define a method will lock the static methods and non-static methods defined by the synchroniezd keyword in the class, but this is a bit difficult to understand. If you want to synchronized blocks to achieve such an effect, it is not difficult to understand why this effect is produced. If you use synchronized to lock all synchronized non-static methods in the class, you only need to use this as the parameter of the synchronized block to pass into the synchronized block. The code is as follows:
public class Test { public void method1() { synchronized(this) { } } public synchronized void method2() { } } public class Test { public void method1() { synchronized(this) { } } public synchronized void method2() { } } In the above code, method1 uses the synchronized block, and the method2 method uses the synchronized keyword to define the method. If you use the same Test instance, as long as one of these two methods is being executed, the other methods will be blocked because they have not obtained the synchronization lock. In addition to using this as the parameter of the synchronized block, you can also use Test.this as the parameter of the synchronized block to achieve the same effect.
In using synchronized blocks in inner classes, this only represents inner classes and has nothing to do with outer classes (OuterClass). However, non-static methods in inner classes and non-static methods in outer classes can also be synchronized. If you add a method method3 to the inner class, you can also synchronize with the two methods in Test, the code is as follows:
public class Test { class InnerClass { public void method3() { synchronized(Test.this){ } } } public class Test { class InnerClass { public void method3() { synchronized(Test.this){ } } } } The method3 method of InnerClass above and the method1 and method2 methods of Test can only have one method executed at the same time.
Whether the synchronized block is executed correctly or the synchronized block is exited due to an exception due to a program error, the synchronization lock held by the current synchronized block will be automatically released. Therefore, you don’t have to worry about the synchronization lock when using the synchronized block.
2. Synchronization of static methods
Since object instances are not necessarily created when calling static methods, this cannot be used to synchronize static methods, but must use Class objects to synchronize static methods. The code is as follows:
public class Test{ public static void method1(){ synchronized(Test.class){ } } public static synchronized void method2(){ } } public class Test{ public static void method1(){ synchronized(Test.class){ } } public static synchronized void method2(){ } }When synchronizing static methods, you can use the static field class of the class to get the class object. In the above example, there is only one method execution of method1 and method2 methods. In addition to using the class field, you can also get the class object through the getClass() method of the instance. The code is as follows:
public class Test{ public static Test test; public Test(){ test=this; } public static void method1(){ synchronized(test.getClass()){ } } public class Test{ public static Test test; public Test(){ test=this; } public static void method1(){ synchronized(test.getClass()){ } } }
In the above code, we obtain an instance of Test through a public static object, and obtain a class object through the getClass method of this instance (note that all instances of a class are obtained through the getClass method). We can also synchronize static methods of different classes through class, the code is as follows:
public class Test1{ public static void method1(){ synchronized(Test.class){ } } } public class Test1{ public static void method1(){ synchronized(Test.class){ } } }
Note: When synchronized blocks are used to synchronize methods, non-static methods can be synchronized through this, while static methods must be synchronized using class objects, but non-static methods can also be synchronized by using class. However, this cannot be used in static methods to synchronize non-static methods. This needs to be paid attention to when using synchronized blocks.
Note
The synchronized keyword will degrade the performance of the application and therefore it can only be used on methods that require modifying shared data in concurrent scenarios. If multiple threads access the same synchronized method, only one thread can access it and the other threads will wait. If the method declaration does not use the synchronized keyword, all threads can execute the method at the same time, thus reducing the total run time. If you are known to be called by more than one thread, you do not need to declare it using the synchronized keyword.
Methods declared synchronized can be called recursively. When a thread accesses an object's synchronization method, it can also call other synchronization methods of the object, including the executing method, without having to obtain access to the method again.
We can protect access to code blocks (rather than the entire method) through the synchronized keyword. The synchronized keyword should be used like this: the rest of the method remains outside the synchronized code block for better performance. Access to the critical section (i.e., code blocks that can only be accessed by one thread at the same time) should be as short as possible. For example, in the operation of obtaining the number of people in a building, we only use the synchronized keyword to protect instructions for updating the number of people, and make other operations not use shared data. When using synchronized keywords in this way, the object reference must be used as an incoming parameter. Only one thread is allowed to access this synchronized code at the same time. Generally speaking, we use this keyword to refer to the object to which the method being executed belongs:
synchronized(this){ //Java code}