理解多线程“锁”的机制

导读:本篇文章讲解 理解多线程“锁”的机制,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

理解“锁”

Java多线程最难理解的锁的机制,下面会通过实例代码来理解

实例一

同一对象调用同步块里的方法

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("打电话!");
    }
}

通过创建一个Phone对象,里面定义两个同步方法,然后创建A/B两个线程来调用下面的方法,判断这两个方法谁先调用!

synchronized同步方法,谁调用他就锁谁,这里我们看到只有一个phone对象去调用它,所以根据代码的编写来看,运行4秒后会先执行发短信,随机执行打电话的方法;因为这两个方法是同一个锁,谁写被拿到谁执行。

实例二

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!");
    }
}

这里Phone2类里有一个发短信的同步方法,还有一个hello普通方法,现在依旧是两个线程,调用发消息和打印hello,谁会先被打印?这里是hello先被打印,因为hello方法并没有被锁,所以在msg方法延迟4秒的时候他不需要等待,只需要按照上面代码定义的延迟两秒打印hello,然后到了第四秒打印消息。

实例三

两个对象执行同步方法

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("打电话!");
    }
}

这里有两个不同的对象,依据谁调用就锁谁的原则,现在phone1调用就锁phone1,这就是一把锁。phone2调用就锁phone2这是另一把锁。所以现在有了两把锁。所以他锁的肯定不是同一个对象,代码就这么走了,phone1去调用发短信的方法,发短信延迟4秒执行,然后phone2是另一把锁,所以phone2并不需要等phone1执行完,他只是等待了2秒就把打电话打印了出来;然后到了第四秒打印了发消息。

实例四

单个对象调用静态同步方法

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("打电话!");
    }
}

加了static关键字之后,概念就完全不一样了,static是静态方法,在类一加载的时候就有了,所以他锁的是class对象,这里两个方法都被static所修饰,所以他们锁的是同一个对象,所以先调用的消息先被打印,然后打印电话。

实例五

两个对象调用静态同步方法

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("打电话!");
    }
}

现在有多了一个对象来调用同步块里的方法,那么谁先打印呢?很显然执行结果依旧不变,因为上面提过static同步块锁的是class对象,phone3虽然被实例两次,但是class对象只有一个。

实例六

单个对象调用静态同步方法与普通同步方法

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("打电话!");
    }
}


到了这就很了然了,摆明了这是两把锁,静态方法块锁的class对象,普通同步方法锁的是调用者。所以后面的打电话不需要去等待前面的方法。所以打电话先执行。

实例七

两个对象调用静态同步方法与普通同步方法

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("打电话!");
    }
}

现在我们就知道套路是什么了,我们研究锁的执行,就先看他们锁的是不是同一个对象,看这里跟上面一样,锁的依然不是一个对象,所以打电话不用等就执行了,执行结果跟上面一样。

原来源:https://www.bilibili.com/video/av90007319?p=10

通过这些例子对多线程锁的理解一定会更为透彻。我本人是看了两遍,加上码这篇文章又复习了一遍。一共是三遍,当你看到狂总问你的题目,你是心里完全有正确思路的时候,就说明你已经掌握了,如果三遍还不行那就五遍八遍,书读百遍其义自见。

Author By 朝花不迟暮

Learn From 狂神说

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/16462.html

(0)
小半的头像小半

相关推荐

半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!