Java: Simple understanding and use of Synchronized in java study notes

Synchronized

0. Background

insert image description here
As shown in the figure above, for example, in the King of Glory program, our team has two threads to count the economy of descendants and Angela respectively.

  • A thread reads the total economy of the current team from the memory and loads it into the local stack of the thread, after performing the +100 operation
  • At this time, the B thread also takes out the economic value + 200 from the memory, and writes 200 back to the memory
  • Just after the execution of thread B, thread A writes 100 back into the memory

There is a problem, our team's economy should be 300, but the memory is 100.

How does synchronized solve this problem?

Two threads, A thread makes the team economy +1, B thread makes the team economy +2, execute a thousand times respectively, the correct result should be 3000, but the result is 2845.

insert image description here
This is the code and console output after locking.

insert image description here

1. What is a synchronized lock?

The synchronized keyword solves the synchronization of resource access between multiple threads. The synchronized keyword can ensure that only one thread can execute the method or code block modified by it at any time.

  • Synchronized is a keyword in Java that can lock code blocks (methods)

    public synchronized void test() {
    
    
        // 关注公众号Java3y
        // doSomething
    }
  • synchronized is a mutex
    • Only one thread is allowed to enter the locked code block at a time
  • synchronized is a built-in lock/monitor lock
    • Every object in Java has a built-in lock (monitor, which can also be understood as a lock mark), and synchronized is used 对象的内置锁(监视器)to lock code blocks (methods)!

In addition, in earlier versions of Java, synchronized was a heavyweight lock, which was inefficient. why?

Because the monitor lock (monitor) is implemented Mutex Lockdepending , Java threads are mapped to the native threads of the operating system. If you want to suspend or wake up a thread, you need the help of the operating system to complete it, and the operating system needs to switch from user mode to kernel mode when switching between threads. The transition between these states takes a relatively long time, and the time cost Relatively high.

Fortunately, after Java 6, Java officials have greatly optimized synchronized from the JVM level, so the current synchronized lock efficiency is also optimized very well. JDK1.6 introduces a lot of optimizations to the implementation of locks, such as spin locks, adaptive spin locks, lock elimination, lock coarsening, biased locks, lightweight locks and other technologies to reduce the overhead of lock operations.

Therefore, you will find that at present, whether it is various open source frameworks or JDK source codes, the synchronized keyword is widely used.

2. Features

  • The first thing to be clear is that Java multithreaded locks are all object-based, and every object in Java can be used as a lock. The class locks we often hear are actually object locks.
  • A Java class has only one Class object (there can be multiple instance objects, and multiple instances share this Class object), and the Class object is also a special Java object. So the class lock we often say is actually the lock of the Class object.
  • synchronized is a mutex that only allows one thread to enter the locked code block at a time
  • Synchronized is a built-in lock monitor lock. Every object in Java has a built-in lock (monitor, which can also be understood as a lock mark), and synchronized uses the built-in lock (monitor) of the object to block code blocks (method) locked! (What is locked is the object, but what we synchronize is the method/code block)

3. Advantages

insert image description here

4. Usage scenarios

The use of Synchronized in Java
insert image description here

Synchronized is generally used to modify three things:

  • 1. Synchronized modification 普通同步方法: the current instance object of the lock object;
  • 2. Synchronized modification 静态同步方法: the lock object is the current Class object;
  • 3. Synchronized modification 同步代码块: the lock object is the object configured in the brackets after Synchronized. This object can be an object (xlock) or a class (Xlock.class);
    insert image description here
public class SynchronizedSample {
    
    
 
    private final Object lock = new Object();
 
    private static int money = 0;
		//非静态方法
    public synchronized void noStaticMethod(){
    
    
        money++;
    }
		//静态方法
    public static synchronized void staticMethod(){
    
    
        money++;
    }
		
    public void codeBlock(){
    
    
      	//代码块
        synchronized (lock){
    
    
            money++;
        }
    }
}

4.1. Modified instance method:

It acts on 当前对象instance locking, and obtains the lock of the current object instance before entering the synchronization code

    @Override
    public synchronized void run() {
    
    
        Date startDate = DateUtil.date();
        for (int i = 0; i < 5; i++) {
    
    
            try {
    
    
                System.out.println("线程 :" + Thread.currentThread().getName() + " 当前计数器 :" + (counter++));
                System.out.println("开始时间 :" + startDate + " 当前时间 :" + DateUtil.date());
                System.out.println();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
    }

4.2. Modified static method:

That is, to 当前类add a lock, it will act on all object instances of the class, and the lock of the current class must be obtained before entering the synchronization code.

  • Because a static member does not belong to any instance object, it is a class member (static indicates that this is a static resource of the class, no matter how many objects are new, there is only one copy).

Therefore, if a thread A calls a non-static synchronized method of an instance object, and thread B needs to call a static synchronized method of the class to which the instance object belongs, it is allowed and mutual exclusion will not occur, because accessing the lock occupied by the static synchronized method It is the lock of the current class, and the lock occupied by accessing the non-static synchronized method is the current instance object lock.

package com.example.lock.syn.demo02;

import cn.hutool.core.date.DateUtil;
import java.util.Date;

/**
 * synchrosnized 关键字测试
 * 同步-静态方法
 */
public class SynchronizedDemo3 implements Runnable {
    
    
    private static int counter = 1;
    
    /**
     * 静态的同步方法
     */
    public synchronized static void method() {
    
    
        Date startDate = DateUtil.date();
        for (int i = 0; i < 5; i++) {
    
    
            try {
    
    
                System.out.println("线程 :" + Thread.currentThread().getName() + " 当前计数器 :" + (counter++));
                System.out.println("开始时间 :" + startDate + " 当前时间 :" + DateUtil.date());
                System.out.println();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
    }
    @Override
    public void run() {
    
    
        method();
    }
}

    public static void main(String[] args) {
    
    
        SynchronizedDemo3 syncThread1 = new SynchronizedDemo3();
        SynchronizedDemo3 syncThread2 = new SynchronizedDemo3();
        Thread thread1 = new Thread(syncThread1, "sync-thread-1");
        Thread thread2 = new Thread(syncThread1, "sync-thread-2");
        thread1.start();
        thread2.start();
    }


insert image description here
Result description

  • syncThread1 and syncThread2 are two objects of SyncThread, but thread synchronization is maintained when thread1 and thread2 are executed concurrently.
  • This is because the static method method is called in run, and the static method belongs to the same class, so syncThread1 and syncThread2 are equivalent to using the same lock.

4.3. Modified code block:

Specify the lock object to lock the given object/class. synchronized(this | object)Indicates that a lock on the given object is to be acquired before entering the synchronized code base.

  • synchronized(类.class)Indicates that the lock of the current class must be obtained before entering the synchronization code
/**
 * synchrosnized 关键字测试
 * 同步代码块
 */
public class SynchronizedDemo1 implements Runnable {
    
    
    /**
     * 全局变量
     * 创建一个计数器
     */
    private static int counter = 1;

    @Override
    public void run() {
    
    
        Date startDate = DateUtil.date();
        synchronized (this) {
    
    
            for (int i = 0; i < 5; i++) {
    
    
                try {
    
    
                    System.out.println("线程 :" + Thread.currentThread().getName() + " 当前计数器 :" + (counter++));
                    System.out.println("开始时间 :" + startDate + " 当前时间 :" + DateUtil.date());
                    System.out.println();
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
    
    
        SynchronizedDemo1 syncThread = new SynchronizedDemo1();
        Thread thread1 = new Thread(syncThread, "sync-thread-1");
        Thread thread2 = new Thread(syncThread, "sync-thread-2");
        thread1.start();
        thread2.start();
    }
}

insert image description here
The results show that when two concurrent threads (thread1 and thread2) access the synchronized code in the same object (syncThread):

  • Only one thread can be executed at the same time, and the other thread is blocked, and the code block must be executed after the current thread finishes executing the code block.
  • Thread1 and thread2 are mutually exclusive, because the current object will be locked when the synchronized code block is executed, the object lock can only be released after the code block is executed, and the next thread can execute and lock the object.

slightly modified

public static void main(String[] args) {
    
    
        SynchronizedDemo1 syncThread1 = new SynchronizedDemo1();
        SynchronizedDemo1 syncThread2 = new SynchronizedDemo1();
        Thread thread1 = new Thread(syncThread1, "sync-thread-1");
        Thread thread2 = new Thread(syncThread2, "sync-thread-2");
        thread1.start();
        thread2.start();
    }

insert image description here
It can be seen from the figure that both threads create a new object to execute, so there are also two locks, so the execution method is executed at the same time.

4.4. Note

  • 1. When using synchronizedthe modified non-static method or using synchronizedthe modified code block as an instance object, different objects of the same class have their own locks, so they will not block each other.
  • 2. synchronizedWhen using modified classes and objects, since class objects and instance objects have their own monitor locks, they will not block each other.
  • 3. When using synchronized to modify an instance object, if a thread is accessing a synchronized method of the instance object, other threads not only cannot access the synchronized method, but also other synchronized methods of the object, because an object has only one monitor lock object, but other threads can access the object's non-synchronized methods.
  • 4. When thread A accesses the non-static synchronized method of the instance object, thread B can also access the static synchronized method of the instance object at the same time, because the former acquires the monitor lock of the instance object, while the latter acquires the monitor lock of the class object , there is no mutually exclusive relationship between the two.

4.5, double check lock implementation object singleton (thread safety)

public class Singleton {
    
    

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    
    
    }

    public  static Singleton getUniqueInstance() {
    
    
       //先判断对象是否已经实例过,没有实例化过才进入加锁代码
        if (uniqueInstance == null) {
    
    
            //类对象加锁
            synchronized (Singleton.class) {
    
    
                if (uniqueInstance == null) {
    
    
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

insert image description here

4.6. Summary

  • The synchronized keyword is added to the static 静态方法and synchronized(class)code blocks to lock the Class class.
  • The synchronized keyword is added to the instance method to lock the object instance.
  • Try not to use synchronized(String a) because in the JVM, the string constant pool has a caching function!

5. The underlying principle

Synchronized analysis - if you are willing to peel off my heart layer by layer

5.1, object header

insert image description here

When a Java object is stored in memory, it consists of the following three parts:

1. Object header

2. Instance data: The instance data of the object is the attributes and their values ​​​​that can be seen in the java code

3. Align padding bytes: Because the JVM requires that the memory size occupied by java objects should be a multiple of 8bit, so there are a few bytes in the back to pad the size of the object to a multiple of 8bit, and there is no special function.

Java's object header consists of the following three parts:

1,Mark Word

2. Pointer to the class: the class data of the Java object is stored in the method area

3. Array length (only for array objects): the length of the data is 32bit in both 32-bit and 64-bit JVM

5.1.1、Mark Word

Detailed explanation of Java's object header and object composition

Mark Word records the information related to the object and the lock. When the object is synchronizedregarded as a synchronization lock by the keyword, a series of operations around the lock are related to the Mark Word.

  • The length of Mark Word in 32-bit JVM is 32bit, and the length in 64-bit JVM is 64bit.
  • Mark Word stores different content in different lock states. It is stored in the 32-bit JVM like this: the lock flag bits are all , but
    insert image description here
    the first 1 bit distinguishes whether it is a lock-free state or a lock-biased state.无锁和偏向锁01

Versions after JDK1.6 have the concept of lock upgrade when dealing with synchronization locks. The JVM starts with biased locks for synchronization locks. As the competition becomes more and more intense, the processing method is upgraded from biased locks to lightweight locks. Eventually upgrade to a heavyweight lock.

JVM generally uses locks and Mark Word like this:

  • 1. 没有锁When, this is an ordinary object, Mark Word records the HashCode of the object, the lock flag bit is 01, and the bit that is biased towards the lock is 0.
  • 2. When the object is regarded as a synchronous lock and 有一个线程A grabs the lock, the lock flag is still 01, but the bit of whether the lock is biased is changed to 1, and the first 23 bits record the thread id that grabbed the lock, indicating that it has entered the biased lock state.
    • When 线程A再次trying to acquire the lock, the JVM finds that the flag of the synchronization lock object is 01whether the biased lock is 1, that is, the biased state. The thread id recorded in MarkWord is the thread A's own id, indicating that thread A has obtained the biased lock. , you can execute code that is synchronously locked.
  • 3. When thread B tries to acquire the lock, the JVM finds that the synchronization lock is in a biased state, but the thread id in the Mark Word record is not B, then thread B will try to acquire the lock first, and the lock acquisition operation here may CAS操作succeed Yes, because thread A generally does not automatically release the biased lock. If the lock is successfully grabbed, change the thread id in the Mark Word to the id of thread B, which means that thread B has obtained the biased lock and can execute the synchronization lock code. If the lock grab fails, go to step 4.
  • 4. If the lock grab fails in the biased lock state, it means that the current lock has certain competition, and the biased lock will be upgraded to a lightweight lock. 开辟一块单独的空间The JVM will save it in the thread stack of the current thread 指向对象锁Mark Word的指针, and at the same time save a pointer to this space in the object lock Mark Word. The above two save operations are both CAS操作. If the save is successful, it means that the thread has grabbed the synchronization lock, and the lock flag in the Mark Word is changed to 00, and the synchronization lock code can be executed. If the save fails, it means that the lock grab failed and the competition is too fierce, go to step 5.
  • 5. If the lightweight lock fails to grab the lock, the JVM will use the spin lock. The spin lock is not a lock state, but just represents continuous retrying and trying to grab the lock. Starting from JDK1.7, the spin lock is enabled by default, and the number of spins is determined by the JVM. If the lock grabbing succeeds, execute the synchronization lock code, and if it fails, proceed to step 6.
  • 6. After retrying the spin lock, if the lock grab still fails, the synchronization lock will be upgraded to a heavyweight lock, and the lock flag will be changed to 10. In this state, threads that have not grabbed the lock will be blocked.

5.2、Monitor

What does java monitor mean, Java interview FAQ: What is the Monitor object?

Monitor can be understood as a synchronization tool or a synchronization mechanism, and is usually described as an object. Every Java object has 有一把an invisible lock, called 内部锁or Monitor锁.

  • Monitor is 线程私有a data structure, each thread has an available monitor recordlist, and there is also a global available list.
  • Each locked object is associated with a monitor, and there is an Owner field in the monitor to store the unique identifier of the thread that owns the lock, indicating that the lock is occupied by this thread.
  • If you use synchronized to the object 加锁(重量级),该对象的Mark Word就被设置指向了Monitor对象的指针

How is the object associated with the monitor? Look directly at the picture:
insert image description here
Monitor structure:
insert image description here

The working mechanism of Java Monitor is shown in the figure:
insert image description here
insert image description here
To make it more vivid, here is an example:

  synchronized(this){
    
      //进入_EntryList队列
            doSth();
            this.wait();  //进入_WaitSet队列
        }

5.3, synchronized synchronization code block

public class SynchronizedDemo {
    
    
	public void method() {
    
    
		synchronized (this) {
    
    
			System.out.println("synchronized 代码块");
		}
	}
}

Use javapthe command to view SynchronizedDemothe relevant bytecode information of the class:

  • First switch to the corresponding directory of the class and execute javac SynchronizedDemo.javathe command to generate the compiled .classfile.
  • Then execute javap -c -s -v -l SynchronizedDemo.class.
    insert image description here
    From the above we can see that:

The implementation of the synchronized block uses the monitorenterand monitorexitbytecode instructions,

monitorenterA directive points to a block of synchronized code 开始位置;
monitorexita directive specifies a block of synchronized code 结束位置.
The JVM must ensure that monitorentry and monitorexit appear in pairs

  • 1. When executing monitorenterthe instruction , the thread tries to acquire the lock, which is to acquire monitorthe ownership of the object monitor.
  • 2. During execution monitorenter, it will try to acquire the lock of the object. If the lock counter is 0, it means that the lock can be acquired. After acquisition, set the lock counter to 1, that is, add 1.
  • 3. After executing monitorexitthe instruction , set the lock counter to 0, indicating that the lock is released. If the acquisition of the object lock fails, the current thread will block and wait until the lock is released by another thread.
  • 4. If the acquisition of monitorthe object fails, the thread will enter a blocked state until other threads release the lock.

5.4, ​​synchronized modification method

public class SynchronizedDemo2 {
    
    
	public synchronized void method() {
    
    
		System.out.println("synchronized 方法");
	}
}

insert image description here
The method modified by synchronized does not have monitorenterinstructions and monitorexitinstructions, but is replaced ACC_SYNCHRONIZEDby the logo , which indicates that the method is a synchronized method.

  • The JVM uses the ACC_SYNCHRONIZED access flag to identify whether a method is declared as a synchronous method, so as to execute the corresponding synchronous call.

5.5, monitorenter, monitorexit, ACC_SYNCHRONIZED instructions

Synchronized analysis - if you are willing to peel off my heart layer by layer

5.6 Summary

1. synchronized 同步语句块The implementation uses the monitorenterand monitorexitdirectives:

  • monitorenterThe instruction points to the start position of the synchronized code block, monitorexitand the instruction indicates the end position of the synchronized code block.

2. synchronized 修饰的方法There is no monitorenter instruction and monitorexit instruction, and instead, ACC_SYNCHRONIZEDthe flag , which indicates that the method is a synchronous method.

3, but both 本质都是对对象监视器 monitor 的获取.
insert image description here

6. Lock optimization

Java Concurrency - Detailed Implementation Principles of Synchronized
insert image description here

7. The difference between synchronized and ReentrantLock

insert image description here

8. The difference between Synchronized and Volatile

  • 1. Performance: The volatile keyword is a lightweight implementation of thread synchronization, so the volatile performance is definitely better than the synchronized keyword .
  • 2. On the scope of modification: the volatile keyword can only be used for variables and the synchronized keyword can be used to modify methods and code blocks.
  • 3. In terms of characteristics: the volatile keyword can guarantee the visibility of the data, but it cannot guarantee the atomicity of the data. The synchronized keyword guarantees both.
  • 4. Function: The volatile keyword is mainly used to solve the visibility of variables among multiple threads, while the synchronized keyword solves the synchronization of accessing resources between multiple threads.

reference

1. Learn about the Java lock mechanism
2. Java thread safety and lock mechanism

Guess you like

Origin blog.csdn.net/JMW1407/article/details/122551213