Java Basics (3)—A simple understanding of the synchronized keyword

1. synchronized modified synchronized code block

Use synchronized to modify the code segment as a synchronization lock. The code is as follows:

public class LockDemo {
    
    
    public Object object = new Object();

    public void show(){
    
    
        synchronized (object) {
    
    
            System.out.println(">>>>>>hello world!");
        }
    }

    public static void main(String[] args) {
    
    
        new LockDemo().show();
    }
}

Directly run the main() method to compile the LockDemo class, then locate the specific LockDemo.class class file path, and javapview the underlying code through the command, as follows:

javap -c LockDemo.class

Insert image description here

From the above figure, we find that the bottom layer is implemented by monitorenter and monitorexit to implement synchronization operations.

But here you will find that one monitorenter corresponds to two monitorexits. Logically speaking, shouldn't locking and unlocking be two operations? Why are there two unlockings? In fact, this design is to ensure that the lock can be released when an exception occurs and the program will not get stuck. But it is not necessarily 1:2, for example, the following code:

public class LockDemo {
    
    
    public Object object = new Object();

    public void show(){
    
    
        synchronized (object) {
    
    
            System.out.println(">>>>>>hello world!");
            throw new RuntimeException(">>>>>>异常");
        }
    }

    public static void main(String[] args) {
    
    
        new LockDemo().show();
    }
}

Use the javap command to view the following diagram:

Insert image description here

Throw the exception to the outermost layer through throwrow, and finally monitorexit releases the lock. The summary is: synchronized locks appear in pairs, but in order to be able to release the locks in abnormal circumstances, good tail processing is added.

2. Syncornized modification method

code show as below:

public class LockDemo {
    
    
    public Object object = new Object();

    public void show(){
    
    
        synchronized (object) {
    
    
            synchronized (this) {
    
    
                System.out.println(">>>>>>hello world!");
            }
        }
    }

    public static synchronized void sop() {
    
    
    
    }

    public static void main(String[] args) {
    
    
        new LockDemo().show();
    }
}

passjavap -v LockDemo.classCommand to view bytecode, as shown below:

Tip : Use java -v Xxx to view more comprehensive information

Insert image description here

Method-level synchronization is invisible and does not need to be controlled by bytecode instructions. It is implemented in method calls and return operations. The virtual machine can know whether this method is declared as a synchronous method from the ACC_SYNCHRONIZED method identifier in the method table structure in the method constant pool . When calling, the calling instruction will check whether the ACC_SYNCHRONIZED access identifier of the method is set. If it is set, the execution thread needs The monitor must be successfully held first , and then the method can be executed. No other thread can obtain the same monitor. If a synchronized method throws an exception during runtime and cannot be handled within the method, the monitor held by the synchronized method will be automatically released when the exception is thrown outside the synchronized method boundary.Montior

3. What is a Monitor?

In the HotSpot virtual machine, Monitor is a mechanism used to achieve synchronization. Monitor can ensure that in a multi-threaded environment, only one thread can access the critical section (shared resources) at the same time, thus ensuring thread safety.

In Java, each object has a Monitor, and the object's Monitor can be obtained through the synchronized keyword. When a thread enters a synchronized block, it will try to obtain the object's Monitor. If the Monitor is being held by another thread, the current thread will be blocked until the Monitor is available. When a thread exits the synchronized block, it releases the object's Monitor so that other threads can obtain the Monitor and enter the synchronized block.

The following is a simple example that demonstrates how to use the synchronized keyword to obtain the Monitor of an object to ensure thread safety:

class Counter {
    
    
    private int count = 0;

    public synchronized void increment() {
    
    
        count++;
    }

    public synchronized void decrement() {
    
    
        count--;
    }

    public synchronized int getCount() {
    
    
        return count;
    }
}

class Main {
    
    
    public static void main(String[] args) {
    
    
        Counter counter = new Counter();

        // 创建 1000 个线程,每个线程分别执行 1000 次增加和减少操作
        for (int i = 0; i < 1000; i++) {
    
    
            new Thread(() -> {
    
    
                for (int j = 0; j < 1000; j++) {
    
    
                    counter.increment();
                    counter.decrement();
                }
            }).start();
        }

        // 等待所有线程执行完毕
        try {
    
    
            Thread.sleep(5000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }

        // 输出最终计数器的值
        System.out.println(counter.getCount()); // 0
    }
}

In the above code, we created a class named Counter, which contains a counter variable count and three synchronized methods increment(), decrement() and getCount(). In these methods, we use the synchronized keyword to obtain the object's Monitor to ensure that operations on the counter are thread-safe in a multi-threaded environment.

In the main method, we created 1000 threads, each of which performed 1000 increments and decrements, resulting in a large number of concurrent modifications to the counter. Finally, we output the value of the counter, expecting to get 0 to make sure everything was done correctly.

The bottom layer of Monitor is implemented using ObjectMonitor.cpp jvm, and each object is born with a Monitor.

See the initialization ObjectMonitor construction method, the source code is as shown below:

Insert image description here

Attributes Property description
_owner Points to the thread holding the ObjectMonitor object
-WaitSet Store the thread queue in wait state
_EntryList Stores the thread queue waiting for the lock block state
_recursions Lock reentry times
_count Used to record the number of times the thread acquires the lock

Insert image description here

If a thread A comes over, enters the EntryList collection, and then tries to acquire the Monitor monitor, it is trying to acquire the lock. After acquiring the lock, it will enter the Owner area, which means that thread A is successfully locked, and then To call the synchronization method code. Assume that when executing the synchronization method, when the wait method is found, thread A will enter the WaitSet queue, and the Owner in the Monitor will also become null, and the number of reentries Recursions = 0, then the new thread that just came over in the EntryList or The threads that are awakened in the WaitSet will start to compete for the Monitor lock. The process is the same as thread A locking.

Guess you like

Origin blog.csdn.net/qq_35971258/article/details/129143827