Concurrent programming synchronized

Posted by hykc on Wed, 09 Mar 2022 11:46:40 +0100

Concurrent programming synchronized

What I said earlier

As I said at the beginning, I want to sort out some learning documents of java Concurrent Programming. This is the third article: synchronized keyword.

It mainly discusses the use of synchronized keywords, lock principle, lock reentry, dirty reading, lock upgrade and other issues.

Welcome to follow and like.

Lat's go

Dirty reading

Let's start with a concept: dirty reading.

The so-called dirty read is to read dirty data.

What is dirty data?

The so-called dirty data is meaningless data or uncertain data.

The situation of dirty reading is basically that one thread reads the data modified by other threads but not completed.

It's like: Ruhua is having trouble with her boyfriend. At this time, Xiaoming takes advantage of the weak point and flirts with Ruhua. Xiaoming thinks he is Ruhua's boyfriend. However, Ruhua goes back to discuss some problems with her boyfriend in the evening and makes up immediately. Then Xiao Ming's self thought is meaningless. This idea is dirty.

OK, let's take an example:

package com.st.sync;

import java.util.concurrent.TimeUnit;

/**
 * @author A programmer in a wig
 * @company Jiangsu jikezhixue - starting point programming
 */
public class SyncDemo0 {
    // Here are the ideas stored like flowers
    private static String info = "Ruhua loves her boyfriend";
    public static void main(String[] args) {
        // Thread 1 indicates the change of Ruhua's own ideas
        new  Thread(()->{
            System.out.println("Ruhua's initial idea:"+info);
            // Start making trouble
            info = "This boyfriend is really not very good......";
            // Start thinking
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // After Ruhua discussed some problems with her boyfriend
            info = "This boyfriend was good when he was good......";
        }).start();
        // Thread 2 indicates Xiao Ming's idea of reading Ruhua
        new Thread(()->{
            // Xiao Ming read the flower like idea
            System.out.println("What Xiao Ming saw:"+info);
            // In the ambiguity between Xiao Ming and Ruhua
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("The last thing Xiao Ming saw:"+info);
        }).start();
    }
}

result:

In this example, the idea Xiao Ming saw for the first time is dirty idea, which is dirty data. Because Ruhua thread is constantly modifying ideas, the idea data read by Xiao Ming in the modification process is uncertain. It makes no sense.

This is called dirty reading. In the above example, you can use the synchronized keyword to prevent dirty reading. The next section will talk about the synchronized keyword.

What is synchronized? How to use it?

The synchronized keyword can lock code (resources).

What do you mean by locking?

Locking is locking. After you go to the toilet, you will lock the toilet door for safety or to avoid embarrassment. At this time, the toilet is exclusive to you. So, the lock is this lock.

Locking in a program is to lock some resources or code. After locking, this resource function is used by the current thread, and other thread functions have to be brought. It's locked.

**First of all, you should know that synchronized locking actually locks the object. The specific object to lock depends on the situation** How to lock objects will be discussed in later chapters.

If you want to say how to use it, let's take a look at the following cases:

**Case 1: * * synchronous static method.

If a synchronized static method is modified synchronized, the Class object of the Class in which the method is located will be locked when the method is executed. Other threads cannot execute any synchronized static methods in the current Class. Of course, the instance method is executable.

Serving:

package com.st.sync;

import java.util.concurrent.TimeUnit;

/**
 * @author A programmer in a wig
 */
public class SyncDemo {
    public static void main(String[] args) {
        // Thread 1, executing synchronous static method 1
        new Thread(()->{
            try {
                method1();
            } catch (InterruptedException e) {
            }
        }).start();
        // Thread 2, executing synchronous static method 2
        new Thread(()->{method2();}).start();
        // Thread 3, execute synchronous non static method
        new Thread(()->{new SyncDemo().method3();}).start();
    }
    // Synchronous static method 1
    public synchronized  static void method1() throws InterruptedException {
        System.out.println("Start of synchronous static method 1");
        // Get a little sleep at first
        TimeUnit.SECONDS.sleep(1);
        System.out.println("End of synchronous static method 1");
    }
    // Synchronous static method 2
    public synchronized  static void method2() {
        System.out.println("Start of synchronous static method 1");
        System.out.println("End of synchronous static method 1");
    }
    // Synchronous instance method
    public synchronized void method3(){
        System.out.println("Synchronous instance method execution");
    }
}

Execution result:

Obviously, after the start of synchronous static method 1, even if it enters the blocking state, the execution of synchronous instance method will not be affected. However, synchronous static method 2 must wait for the complete end of synchronous static method 1 and release the lock of Class object before execution.

What? Do you want to ask if asynchronous methods will be affected??? I refuse to answer this because it is not in the category of concurrent programming

**Case 2: * * synchronization instance method

If you use synchronized to modify the instance method, when a thread executes the synchronized instance method, the instance method caller (this) will be locked. Other threads cannot call other synchronous instance methods of this object.

Serving:

/**
 * @author A programmer in a wig
 */
public class SyncDemo1 {
    public static void main(String[] args) {
        // Instance object
        SyncDemo1 sd = new SyncDemo1();
        // Thread 1, execute synchronization instance method 1
        new Thread(()->{
            try {
                sd.method1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        // Thread 2, execute synchronization instance method 2
        new Thread(()->{sd.method2();}).start();
    }

    // Synchronization instance method 1
    public synchronized void method1() throws InterruptedException {
        System.out.println("Synchronization instance method 1 starts");
        // Get some sleep
        TimeUnit.SECONDS.sleep(2);
        System.out.println("End of synchronization instance method 1");
    }
    // Synchronization instance method 2
    public synchronized void method2(){
        System.out.println("Synchronous instance method 2 execution");
    }
}

result:

Obviously, even if synchronous instance method 1 enters blocking, synchronous instance method 2 must wait for instance method 1 to finish before executing. Because the sd object is locked.

**Case 3: * * synchronization code block

Synchronized code blocks are easy to understand. Synchronized (object) locks the object in ().

Serving:

package com.st.sync;

import java.util.concurrent.TimeUnit;

/**
 * @author A programmer in a wig
 * @company Jiangsu jikezhixue - starting point programming
 */
public class SyncDemo2 {
    public static void main(String[] args) {
        // The guy ready to lock
        Object obj = new Object();
        // Thread 1, lock obj
        new Thread(()->{
            synchronized (obj){
                System.out.println("Thread 1, locked obj");
                // Take a nap
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread 1, ready to release obj");
            }
        }).start();
        // Thread 2, also lock obj
        new Thread(()->{
            synchronized (obj){
                System.out.println("Scene 2, locked obj");
            }
        }).start();
    }
}


Thread 2 cannot continue until thread 1 releases obj.

OK, a little summary:

When synchronized modifies a static method, the Class object is locked.

When synchronized modifies the instance method, the lock is the this object.

When synchronized modifies a code block, the lock is the object in ().

What does it mean to lock the object to low? Look at the next section.

How do you lock objects when synchronized to low?

synchronized to low is how to lock objects. It's about locking the object's head. Let's see my explanation:

java objects are a string of binary in memory, As for the "lock position" of the object string, it is the mark of "how to change the last three bits of the lock structure". That is, when the "lock position" of the object string changes, it is the mark of "01" of the last three bits of the lock structure There are detailed descriptions in "lock upgrade".

Therefore, to lock this object is to try to change the value of the lock flag bit of this object. Successful change means successful lock acquisition.

For example:

Lock status:

Unlock status:

The so-called lock object is probably like this. It should not be difficult to understand.

synchronized lock upgrade

The key to synchronized is locking, in jdk1 Before 6, it was a heavyweight lock. In jdk1 After 6, it is optimized and will be upgraded automatically according to the situation.

What is a heavyweight lock?

The so-called heavyweight lock is that you lock the door when you go to the bathroom. Your roommate comes to have a look and leaves directly when the door is locked. After you go to the bathroom, inform him to go to the bathroom.

The corresponding one is a lightweight lock, which is also an optimistic lock. When you lock the door when you go to the toilet, you are also very anxious. You don't dare to take a single second, so he stays at the door of the toilet and keeps asking, "OK? Are you ready ". as soon as you are well, he will rush into the toilet at the first time. (this is re-entry, which is realized by CAS)

OK, let's talk about lock upgrade.

In jdk1 6. At the beginning, the objects are divided into: unlocked state, biased lock state, lightweight lock state and heavyweight lock state.

As mentioned in the last section. The last three bits in the object header are the lock identification bits. This is generally the case.

Lock flag bitLock state
0 01Unlocked state
1 01Bias lock state
1 00Lightweight lock state
1 11Heavyweight lock status

Of course, you can output the object header on the console and observe the object header. (this is easy to do with third-party tools). But you should see that the data change of lock upgrade may be a little troublesome. I'm really not ready to write a test program for lock upgrade in this document today. If you need to leave a message, I'll add one.

Next, let's talk about how synchronized locks are upgraded.

Professional description: the newly created object is in a lock free state. When a thread uses it, the object will become in a lock biased state. When there is a small amount of competition, it will be automatically adjusted to lightweight lock. When there are more concurrency and competition, it will be adjusted to heavyweight locks.

My description is as follows:

  • Unlocked state: Xiao Ming and Ruhua are just ordinary friends. Xiao Ming can have an unrestricted affair with many girls.

  • Partial lock state: Xiaoming and Ruhua have determined the relationship between boyfriend and girlfriend. Xiaoming only has an affair with Ruhua, and there are no other girls looking for Xiaoming.

  • Lightweight lock: Xiaoming and Ruhua have a clear relationship, but Xiaoming meets poetry, Shanshan, Meimei, Qianqian.... Each makes Xiao Ming excited. So Ruhua seized Xiao Ming's crime tool "mobile phone" in a rage. But even so, these poems, Shanshan and Meimei, still come to Xiao Ming from time to time.

  • Heavyweight lock: due to Xiaoming's charm, there are too many kinds of poems by Xiaoming. Ruhua took Xiaoming to get his marriage certificate for safety. And the world declares that Xiaoming is now her private resource. In response to the pursuit of Xiaoming, we must wait until she and Xiaoming divorce.

The synchronized lock upgrade is handled automatically, which is roughly the above situation. If you still don't understand, you are welcome to ask questions.

Lock reentry

The so-called lock reentry is that a thread has acquired a lock and tries to acquire the same lock again for various reasons, which is lock reentry.

Synchronized supports lock reentry. The example is very simple. Is to try to call another synchronization method in one synchronization method.

Take a case:

package com.st.sync;

/**
 * @author A programmer in a wig
 * @company Jiangsu jikezhixue - starting point programming
 */
public class SyncDemo3 {
    public static void main(String[] args) {
        method1();
    }
    // Synchronous static method 1
    public synchronized static void method1(){
        System.out.println("Synchronous static method 1");
        // Call static synchronization method 2
        method2();
    }
    // Synchronous static method 2
    public synchronized static void method2(){
        System.out.println("Static synchronization method 2");
    }
}

We call static synchronization method 2 in synchronous static method 1, which is certainly feasible. Because both methods are synchronous static methods, the two methods lock the same Class object. Therefore, when synchronous method 2 is called, it is the re-entry of the lock.

That's all I have to say about synchronized... What else need to be added? Welcome to leave a message

Topics: Java Concurrent Programming JUC