Understand the mechanism of multi-threaded "lock"

Understanding "lock"

The most difficult to understand locking mechanism of Java multithreading, the following will understand through the example code

Example one

The same object calls the method in the synchronized block

package com.leo.lock8;

import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: Leo
 * @createDate: 2020/2/27
 * @version: 1.0
 */
public class Test01
{
    
    
    public static void main(String[] args)
    {
    
    
        Phone phone = new Phone();
        new Thread(() ->
        {
    
    
            phone.sendMsg();
        },"A").start();
        try
        {
    
    
            TimeUnit.SECONDS.sleep(1);//延时1秒
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        new Thread(()->{
    
    phone.call();},"B").start();
    }
}

class Phone
{
    
    
    public synchronized void sendMsg()
    {
    
    
        try
        {
    
    
            TimeUnit.SECONDS.sleep(4);//延时4秒
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        System.out.println("发送消息!");
    }

    public synchronized void call()
    {
    
    
        System.out.println("打电话!");
    }
}

By creating a Phone object, defining two synchronization methods, and then creating A/B two threads to call the following methods to determine who calls these two methods first!

Synchronized synchronization method, whoever calls him will lock whoever, here we see that there is only one phone object to call it, so according to the writing of the code, after running for 4 seconds, it will execute the text message first, and the method of calling randomly; because of this The two methods are the same lock, and whoever writes is executed by whoever gets it.

Example two

package com.leo.lock8;

import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: Leo
 * @createDate: 2020/2/27
 * @version: 1.0
 */
public class Test02
{
    
    
    //发短信和hello谁会先执行?
    public static void main(String[] args)
    {
    
    
        Phone2 phone = new Phone2();
        new Thread(() ->
        {
    
    
            phone.sendMsg();
        }, "A").start();
        try
        {
    
    
            TimeUnit.SECONDS.sleep(2);
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        new Thread(() ->
        {
    
    
            phone.hello();
        }, "B").start();
    }
}

class Phone2
{
    
    
    public synchronized void sendMsg()
    {
    
    
        try
        {
    
    
            TimeUnit.SECONDS.sleep(4);
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        System.out.println("发送消息!");
    }

    public void hello(){
    
    
        System.out.println("hello!");
    }
}

Here there is a synchronous method for sending text messages in the Phone2 class, and a normal method for hello. Now there are two threads. Calling to send messages and print hello, who will be printed first? Here hello is printed first, because the hello method is not locked, so when the msg method is delayed for 4 seconds, he does not need to wait, just print hello according to the delay defined by the above code for two seconds, and then print the message in the fourth second.

Example three

Two objects execute synchronous methods

package com.leo.lock8;

import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: Leo
 * @createDate: 2020/2/27
 * @version: 1.0
 */
public class Test02
{
    
    
    //发短信和hello谁会先执行?
    public static void main(String[] args)
    {
    
    
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();
        new Thread(() ->
        {
    
    
            phone1.sendMsg();
        }, "A").start();
        try
        {
    
    
            TimeUnit.SECONDS.sleep(2);
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        new Thread(() ->
        {
    
    
            phone2.call();
        }, "B").start();
    }
}

class Phone2
{
    
    
    public synchronized void sendMsg()
    {
    
    
        try
        {
    
    
            TimeUnit.SECONDS.sleep(4);
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        System.out.println("发送消息!");
    }

    public synchronized void call()
    {
    
    
        System.out.println("打电话!");
    }
}

There are two different objects here. According to the principle of whoever calls, locks whoever is called. Now phone1 is locked when phone1 is called. This is a lock. Call phone2 to lock phone2. This is another lock. So now there are two locks. Therefore, he must not lock the same object. The code is gone. Phone1 calls the method of sending text messages. The text message is executed after 4 seconds. Then phone2 is another lock, so phone2 does not need to wait for phone1 to finish executing. After waiting for 2 seconds, the call was printed out; then in the fourth second, the message was printed out.

Example four

A single object calls a static synchronization method

package com.leo.lock8;

import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: Leo
 * @createDate: 2020/2/27
 * @version: 1.0
 */
public class Test03
{
    
    
    //
    public static void main(String[] args)
    {
    
    
        //现在有两个对象,谁会先执行?
        Phone3 phone = new Phone3();
        new Thread(() ->
        {
    
    
            phone.sendMsg();
        }, "A").start();
        try
        {
    
    
            TimeUnit.SECONDS.sleep(2);
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        new Thread(() ->
        {
    
    
            phone.call();
        }, "B").start();
    }
}

class Phone3
{
    
    
    public static synchronized void sendMsg()
    {
    
    
        try
        {
    
    
            TimeUnit.SECONDS.sleep(4);
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        System.out.println("发送消息!");
    }

    public static synchronized void call()
    {
    
    
        System.out.println("打电话!");
    }
}

After adding the static keyword, the concept is completely different. Static is a static method, which is present when the class is loaded, so it locks the class object. Here two methods are modified by static, so they lock Is the same object, so the message that is called first is printed first, and then the phone is printed.

Example 5

Two objects call a static synchronization method

package com.leo.lock8;

import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: Leo
 * @createDate: 2020/2/27
 * @version: 1.0
 */
public class Test03
{
    
    
    //
    public static void main(String[] args)
    {
    
    
        //现在有两个对象,谁会先执行?
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();
        new Thread(() ->
        {
    
    
            phone1.sendMsg();
        }, "A").start();
        try
        {
    
    
            TimeUnit.SECONDS.sleep(2);
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        new Thread(() ->
        {
    
    
            phone2.call();
        }, "B").start();
    }
}

class Phone3
{
    
    
    public static synchronized void sendMsg()
    {
    
    
        try
        {
    
    
            TimeUnit.SECONDS.sleep(4);
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        System.out.println("发送消息!");
    }

    public static synchronized void call()
    {
    
    
        System.out.println("打电话!");
    }
}

Now there is one more object to call the method in the synchronized block, so who prints first? Obviously the execution result remains unchanged, because the static synchronization block mentioned above locks the class object. Although phone3 is instantiated twice, there is only one class object.

Example 6

Single object calls static synchronization methods and ordinary synchronization methods

package com.leo.lock8;

import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: Leo
 * @createDate: 2020/2/27
 * @version: 1.0
 */
public class Test04
{
    
    
    //
    public static void main(String[] args)
    {
    
    
        //现在有两个对象,谁会先执行?
        Phone4 phone = new Phone4();
        new Thread(() ->
        {
    
    
            phone.sendMsg();
        }, "A").start();
        try
        {
    
    
            TimeUnit.SECONDS.sleep(2);
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        new Thread(() ->
        {
    
    
            phone.call();
        }, "B").start();
    }
}

class Phone4
{
    
    
    public static synchronized void sendMsg()
    {
    
    
        try
        {
    
    
            TimeUnit.SECONDS.sleep(4);
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        System.out.println("发送消息!");
    }

    public synchronized void call()
    {
    
    
        System.out.println("打电话!");
    }
}


When it comes to this, it is very clear. It is clear that these are two locks, the class object of the static method block lock, and the normal synchronous method locks the caller. Therefore, subsequent calls do not need to wait for the previous method. So call it first.

Example 7

Two objects call static synchronization method and ordinary synchronization method

package com.leo.lock8;

import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: Leo
 * @createDate: 2020/2/27
 * @version: 1.0
 */
public class Test04
{
    
    
    //
    public static void main(String[] args)
    {
    
    
        //现在有两个对象,谁会先执行?
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        new Thread(() ->
        {
    
    
            phone1.sendMsg();
        }, "A").start();
        try
        {
    
    
            TimeUnit.SECONDS.sleep(2);
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        new Thread(() ->
        {
    
    
            phone2.call();
        }, "B").start();
    }
}

class Phone4
{
    
    
    public static synchronized void sendMsg()
    {
    
    
        try
        {
    
    
            TimeUnit.SECONDS.sleep(4);
        }
        catch (InterruptedException e)
        {
    
    
            e.printStackTrace();
        }
        System.out.println("发送消息!");
    }

    public synchronized void call()
    {
    
    
        System.out.println("打电话!");
    }
}

Now we know what the routine is. When we study the execution of the lock, we first see if they lock the same object. See here as above, the lock is still not an object, so the call is executed without waiting. The result is the same as above.

Original source: https://www.bilibili.com/video/av90007319?p=10

Through these examples, the understanding of multithreaded locks will definitely be more thorough. I personally read it twice and reviewed this article with the code. There are three times in total. When you see the question that the mad always asks you, and you have the correct idea in your heart, it means that you have mastered it. If the three times are not enough, then five or eight times, and the book read a hundred times. Righteousness.

Author By Chao Hua is not late

Learn From Mad God says

Insert picture description here

Guess you like

Origin blog.csdn.net/Curtisjia/article/details/104548102