您当前的位置: 首页 > 

Dongguo丶

暂无认证

  • 2浏览

    0关注

    472博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

多线程锁的8种情况(经典8锁问题)

Dongguo丶 发布时间:2021-09-16 08:58:51 ,浏览量:2

案例以synchronized为例:

使用手机发短信或发邮件

注意两个点:锁的对象以及锁的范围

案例1先打印短信还是邮件

手机有发送短信和发邮件的功能,创建两个线程分别发送短信和邮件,是先打印短信还是邮件呢?

package com.dongguo.synclock;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-12:46
 * @description:
 */
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Phone phone1 = new Phone();

        new Thread(()->{
            try {
                phone1.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadA").start();
        //保证先执行ThreadA
        Thread.sleep(100);
        new Thread(()->{
            try {
                phone1.sendEmail();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadB").start();
    }
}

class Phone {
    public synchronized void sendSMS() throws Exception {
        System.out.println("------sendSMS");
    }

    public synchronized void sendEmail() throws Exception {
        System.out.println("------sendEmail");
    }

    public void getHello() {
        System.out.println("------getHello");
    }
}
运行结果:
------sendSMS
------sendEmail

sendSMS,sendEmail依次打印

案例二:停4秒在短信方法内,先打印短信还是邮件
package com.dongguo.synclock;

import java.util.concurrent.TimeUnit;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-12:46
 * @description:
 */
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Phone phone1 = new Phone();

        new Thread(()->{
            try {
                phone1.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadA").start();
        Thread.sleep(100);
        new Thread(()->{
            try {

                phone1.sendEmail();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadB").start();
    }
}

class Phone {
    public  synchronized void sendSMS() throws Exception {
         //停留四秒
        TimeUnit.SECONDS.sleep(4);
        System.out.println("------sendSMS");
    }

    public synchronized void sendEmail() throws Exception { 
        System.out.println("------sendEmail");
    }

    public void getHello() {
        System.out.println("------getHello");
    }
}
运行结果:
------sendSMS
------sendEmail

线程阻塞了4秒后sendSMS,sendEmail依次打印

案例1,2锁的都是当前对象Phone1,同一时刻,只能有唯一一个线程去访问这些synchronized方法

ThreadA先访问sendSMS方法获取锁,ThreadB必须等到ThreadA释放锁才能访问sendEmail方法

一个对象里面如果有多个synchronized方法,某一个时刻内,只要个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一的个线程去访问这些synchronized方法

锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法

案例三:新增普通的hello方法,是先打短信还是hello
package com.dongguo.synclock;

import java.util.concurrent.TimeUnit;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-12:46
 * @description:
 */
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Phone phone1 = new Phone();

        new Thread(()->{
            try {
                phone1.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadA").start();
        Thread.sleep(100);
        new Thread(()->{
            try {

//                phone1.sendEmail();
                phone1.getHello();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadB").start();
    }
}

class Phone {
    public  synchronized void sendSMS() throws Exception {
        //停留四秒
        TimeUnit.SECONDS.sleep(4);
        System.out.println("------sendSMS");
    }

    public synchronized void sendEmail() throws Exception {
        System.out.println("------sendEmail");
    }

    public void getHello() {
        System.out.println("------getHello");
    }
}
运行结果:
------getHello
------sendSMS

getHello是非同步方法,在线程A阻塞时,线程B会执行getHello(),线程A阻塞4秒后执行sendSMS()

加个普通方法后发现和同步锁无关

案例4:现在有两部手机,先打印短信还是邮件
package com.dongguo.synclock;

import java.util.concurrent.TimeUnit;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-12:46
 * @description:
 */
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->{
            try {
                phone1.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadA").start();
        Thread.sleep(100);
        new Thread(()->{
            try {

                phone2.sendEmail();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadB").start();
    }
}

class Phone {
    public  synchronized void sendSMS() throws Exception {
        //停留四秒
        TimeUnit.SECONDS.sleep(4);
        System.out.println("------sendSMS");
    }

    public synchronized void sendEmail() throws Exception {
        System.out.println("------sendEmail");
    }

    public void getHello() {
        System.out.println("------getHello");
    }
}
运行结果:
------sendEmail
------sendSMS

线程A使用phone1发送短信,线程B使用phone2发送邮件,由于使用的不是同一个锁

线程B先执行sendEmail,线程A阻塞4秒后执行sendSMS

有两个Phone对象,两个线程锁的不是同一个Phone对象

换成两个对象后,不是同一把锁了。

案例5:两个静态同步方法,1部手机,先打印短信还是邮件
package com.dongguo.synclock;

import java.util.concurrent.TimeUnit;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-12:46
 * @description:
 */
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->{
            try {
                phone1.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadA").start();
        Thread.sleep(100);
        new Thread(()->{
            try {

                phone1.sendEmail();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadB").start();
    }
}

class Phone {
    public static   synchronized void sendSMS() throws Exception {
        //停留四秒
        TimeUnit.SECONDS.sleep(4);
        System.out.println("------sendSMS");
    }

    public static synchronized void sendEmail() throws Exception {
        System.out.println("------sendEmail");
    }

    public void getHello() {
        System.out.println("------getHello");
    }
}
运行结果:
------sendSMS
------sendEmail
案例6:两个静态同步方法,2部手机,先打印短信还是邮件
package com.dongguo.synclock;

import java.util.concurrent.TimeUnit;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-12:46
 * @description:
 */
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->{
            try {
                phone1.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadA").start();
        Thread.sleep(100);
        new Thread(()->{
            try {

                phone2.sendEmail();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadB").start();
    }
}

class Phone {
    public static   synchronized void sendSMS() throws Exception {
        //停留四秒
        TimeUnit.SECONDS.sleep(4);
        System.out.println("------sendSMS");
    }

    public static synchronized void sendEmail() throws Exception {
        System.out.println("------sendEmail");
    }

    public void getHello() {
        System.out.println("------getHello");
    }
}
运行结果:
------sendSMS
------sendEmail

案例5,6都是线程阻塞4秒后依次打印

静态同步方法锁的是Phone.Class,而不是具体的Phone1,Phone1对象

对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部部手机,所有的普通同步方法用的都是同一把锁一实例对象本身,对于静态同步方法,锁的是当前类的Class对象,如phone.class唯一的一个模板一对于同步方法块,锁的是synchronized括号内的对象

案例7:1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
package com.dongguo.synclock;

import java.util.concurrent.TimeUnit;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-12:46
 * @description:
 */
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->{
            try {
                phone1.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadA").start();
        Thread.sleep(100);
        new Thread(()->{
            try {

                phone1.sendEmail();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadB").start();
    }
}

class Phone {
    public static  synchronized void sendSMS() throws Exception {
        //停留四秒
        TimeUnit.SECONDS.sleep(4);
        System.out.println("------sendSMS");
    }

    public  synchronized void sendEmail() throws Exception {
        System.out.println("------sendEmail");
    }

    public void getHello() {
        System.out.println("------getHello");
    }
}
运行结果:
------sendEmail
------sendSMS

sendEmail先打印,等待4秒打印出sendSMS

案例8:1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件
package com.dongguo.synclock;

import java.util.concurrent.TimeUnit;

/**
 * @author Dongguo
 * @date 2021/8/24 0024-12:46
 * @description:
 */
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->{
            try {
                phone1.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadA").start();
        Thread.sleep(100);
        new Thread(()->{
            try {

                phone2.sendEmail();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"ThreadB").start();
    }
}

class Phone {
    public static  synchronized void sendSMS() throws Exception {
        //停留四秒
        TimeUnit.SECONDS.sleep(4);
        System.out.println("------sendSMS");
    }

    public  synchronized void sendEmail() throws Exception {
        System.out.println("------sendEmail");
    }

    public void getHello() {
        System.out.println("------getHello");
    }
}
运行结果:
------sendEmail
------sendSMS

与案例7一样

两个线程锁的对象不一样,不存在锁竞争

当一个线程试图访间同步代码时它首先必须得到锁,退出或地出异常时必须释放锁。所有的普通同步方法用的都是同一把锁—实例对象本字,就是newl来的具体实例对象本身,本类this也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁。所有的静态同步方法用的也是同一把锁一类对象本身,就是我们说过的唯一模板class具体实例对象this和唯一模成class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。

总结

synchronized实现同步的基础:Java中的每一个对象都可以作为锁。

具体表现为以下3种形式。

,对于普通同步方法,锁是当前实例对象。

对于静态同步方法,锁是当前类的Class对象。

对于同步方法块,锁是Synchonized括号里配置的对象

关注
打赏
1638062488
查看更多评论
立即登录/注册

微信扫码登录

0.0471s