synchronized & monitor lock

1. synchronized & monitor lock

1.1 Introduction to synchronized

In Java, synchronized is a keyword used to implement thread synchronization and mutual exclusion control. It can decorate methods or code blocks to protect access to shared resources and avoid concurrency problems caused by multiple threads modifying data at the same time.

Specifically, the synchronized keyword works as follows:

  1. Mutual exclusion: The synchronized keyword ensures that only one thread can execute the method or code block modified by synchronized at the same time. When a thread enters a synchronized code block, it tries to acquire the object's monitor lock (internal lock). If the lock is already occupied by other threads, the current thread will be blocked until the lock is acquired to execute, thus ensuring mutual exclusive access of multiple threads to shared resources.

  2. Visibility: The synchronized keyword not only provides mutual exclusion, but also ensures that modifications made by a thread to a shared variable before releasing the lock are visible to other threads. This means that when a thread modifies a shared variable protected by synchronized, other threads can see the latest value after acquiring the lock, avoiding the problem of data inconsistency.

Using the synchronized keyword can effectively prevent concurrency problems such as data competition and race conditions caused by multiple threads accessing shared resources at the same time. It is one of the most commonly used synchronization mechanisms in Java.

When using synchronized, you need to pay attention to the following points:

  1. Code blocks that can protect shared resources should be as narrow as possible to avoid unnecessary lock competition.

  2. The synchronized keyword can be used on instance methods, static methods and code blocks. For instance methods and code blocks, the lock object is the current instance; for static methods and code blocks, the lock object is the Class object of the current class.

  3. If multiple threads access different object instances, the locks between them are not mutually exclusive. In other words, synchronized isolates concurrent access between different object instances.

  4. In some cases, using the Lock interface and its implementation classes (such as ReentrantLock) can provide more flexible and fine-grained lock control, but the lock needs to be released manually.

In short, the synchronized keyword is an important tool in Java for thread synchronization and mutual exclusion control. By ensuring the exclusivity and visibility of critical sections, it effectively protects shared resources and improves the security and correctness of multi-threaded programs. .

1.2 Monitor lock (Monitor Lock is also called internal lock or mutex)

When using keywords synchronized, the concept of locks is involved. For synchronizedcode blocks or methods, locks are a mechanism used to protect shared resources.

In Java, every object has a monitor lock (also known as an intrinsic lock or mutex) associated with it. When a thread wants to enter a modified synchronizedcode block or method, it must first acquire the object's lock before executing the code block or method.

Object object2 = new Object();

synchronized (object2) {
    // 同步代码块
    // 这里的锁就是对象object2
    // 只有获得object2的锁才能执行这段同步代码块
}

In the above code, an object2object named is used as the lock object. Only when the lock is acquired object2, that is, the monitor lock of the object is successfully acquired , can the content in the code block object2be executed . synchronizedOther object2threads attempting to acquire the lock are blocked until the lock is released.

Therefore, the code in this code fragment synchronized (object2)means that only one thread can enter the code block object2associated with it at the same time synchronized, and other threads need to wait for the lock to be released before continuing to execute.

Note that the lock object can be any object, "object2" here is just an example variable name. It is important to share the same lock object among multiple threads to achieve thread synchronization and mutual exclusion control.

2. Thread safety


2.1 Thread Safety Demonstration


If multiple threads jointly access member variables (shared) and modify them, there will be thread safety issues.

As follows: There are 20 beans on the table, each thread takes 1 bean on the table, and the number of remaining beans is displayed on the table.

When the number of beans remaining on the table is 0, an exception is thrown.

package day06.threadDemo;
 
public class Demo6 {
    public static void main(String[] args) {
        Table table = new Table();
        Table.Person p1 = table.new Person();
        Table.Person p2 = table.new Person();
        p1.start();
        p2.start();
 
    }
 
}
 
class Table{
    int beans = 20;
    public int getBeans(){
        if(beans ==0){
            throw new RuntimeException("豆没了");
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return beans--;
    }
 
    class Person extends Thread{
        @Override
        public void run() {
            while (true){
                int beans = getBeans();
                System.out.println(this.getName()+":"+beans);
            }
        }
    }
}

Results of the:

 When the remaining beans on the table are not 0, go to the table to get the beans.

When there is 1 bean left on the table, thread 0 and thread 1 will go to the table to get the bean at the same time (there is a probability problem here, we have opened two threads, they do not necessarily get the last bean at the same time. If not at the same time There will be no thread safety problems. If two threads take the last one at the same time), thread 0 takes the last bean, and thread 1 also takes one, and then the table will be There is -1. 

2.2 Solving thread safety issues


1. Do not access common variables if you can not access them, and do not modify them if you can.

2. Locking (but it will affect performance)

public synchronized int  getBeans()

Lock the getBeans method.

After a thread enters this method, other threads cannot enter. After the thread executes the program, the lock is released, and other threads can use this program (for a disgusting example, multiple people go to the toilet and enter a toilet). After people, others will queue up)

2.3 Method locking


Add the keyword synchronized to the method

    public synchronized int  getBeans(){
        if(beans ==0){
            throw new RuntimeException("豆没了");
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return beans--;
    }


Equivalent to

    public int getBean(){
        synchronized (this){
            if(beans ==0){
                throw new RuntimeException("豆没了");
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return beans--;
        }
        }

 In synchronized (), to execute the resource (specifically which object's lock), this is its own object.
Lock the entire method.

Overall code:

Here is the resource that uses its own Table object as the lock.

package com.example.analyzestack.controller;



public class SynchronizedTest {
    public static void main(String[] args) {
        Table table = new Table();
        Table.Person p1= table.new Person();
        Table.Person p2 = table.new Person();
        p1.start();
        p2.start();
    }

}


class Table{
    int beans =20;
    public synchronized int getBeans() {
        // synchronized  加锁
        if(beans==0){
            throw new RuntimeException("豆子没有了");
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return beans--;
    }

    class Person extends Thread{
        @Override
        public void run() {
            while (true){
                int beans =getBeans();
                System.out.println(this.getName()+":"+beans);
            }
        }
    }
}

2.4 Static method locking


Static method, the default is to use the class of the class as the lock

    // 静态方法,默认使用的是类的class当锁(Table.class)
    public static synchronized void test1(){
        synchronized (Table.class){
            
        }
    }


2.5 Invalid locking


When we lock, we lock the same object. like this situation

    public synchronized int  getBeans(){
        Object obj = new Object();
        synchronized (obj){
            if(beans ==0){
                throw new RuntimeException("豆没了");
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return beans--;
 
        }


Lock obj, obj is not an object of our class, and an obj will be created every time the getBeans method is called. This lock is useless.

2.6 Deadlock


Two threads, thread 1 occupies lock A and goes to lock B. Thread 2 occupies lock B and goes to lock A. Each other cannot get resources, resulting in a deadlock.

for example

package day06.threadDemo;
 
public class Demo8 {
    public static void main(String[] args) {
        Boo boo = new Boo();
        Thread t1 = new Thread(){
            @Override
            public void run() {
                boo.test1();
            }
        };
 
        Thread t2 = new Thread(){
            @Override
            public void run() {
                boo.test2();
            }
        };
        t1.start();
        t2.start();
 
    }
}
 
class Boo{
    private Object object1 = new Object();
    private Object object2 = new Object();
 
    public void test1(){
        synchronized (object1){
            System.out.println(Thread.currentThread().getName()+"-obj1");
            synchronized (object2){
                System.out.println(Thread.currentThread().getName()+"-obj2");
            }
        }
    }
 
    public void test2(){
        synchronized (object2){
            System.out.println(Thread.currentThread().getName()+"-obj2");
            synchronized (object1){
                System.out.println(Thread.currentThread().getName()+"-obj1");
            }
        }
    }
}

Running this program, the program has been executed forever. At this point, it is deadlocked.

Guess you like

Origin blog.csdn.net/qq_39208536/article/details/131472319
Recommended