八锁现象——synchronized关键字

八锁现象——synchronized关键字

学习目标: 什么是锁?锁是什么?如何判断锁的是谁

八种锁

1、一个对象,俩个同步方法

2、一个对象,俩个同步方法,一个方法延迟

3、两个对象,两个同步方法

4、一个对象,一个同步,一个普通方法

5、一个对象,俩个静态同步方法

6、两个对象,俩个静态同步方法

7、一个对象,一个静态的同步方法,一个同步方法

8、两个对象,一个静态的同步方法,一个同步方法

第一锁

两个线程调用Phone执行Phone下的发短信和打电话方法

1、标准情况下,两个线程先执行发短信,再打电话。

结果:发短信先执行

import java.util.concurrent.TimeUnit;

/**
 * 八锁,就是关于锁的八个问题
 * 1、标准情况下,两个线程先执行发短信,再打电话
 */
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();

        // 线程A发短信
        new Thread(()->{phone.sendSms();},"A").start();
        // 休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 线程B打电话
        new Thread(()->{phone.call();},"B").start();
    }
}
class Phone{
    // 发短信
    public synchronized void sendSms(){
        System.out.println("发短信");
    }
    // 打电话
    public synchronized void call(){
        System.out.println("打电话");
    }
}
第二锁

2、sendSms方法延时4秒,仍然是先执行发短信,再打电话。所以我们排除执行顺序是按照线程创建的先后顺序

结果:发短信先执行

import java.util.concurrent.TimeUnit;

/**
 * 八锁,就是关于锁的八个问题
 * 2、sendSms方法延时4秒,仍然是先执行发短信,再打电话
 */
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();

        // 线程A发短信
        new Thread(()->{phone.sendSms();},"A").start();
        // 休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 线程B打电话
        new Thread(()->{phone.call();},"B").start();
    }
}
class Phone{
    // 发短信
    public synchronized void sendSms(){
        // 休息四秒
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    // 打电话
    public synchronized void call(){
        System.out.println("打电话");
    }
}
第一、二锁解释
被synchronized修饰的方法,锁的对象是方法的调用者,此方法中均是对象Phone来调用的,即两个方法调用的对象是同一个、使用的同一把锁,谁先拿到就谁先执行。

第三锁

3、phone、phone1两个不同对象在两个线程A、B里分别调用发短信、打电话,其中发短信方法有4秒休眠。

结果:打电话先执行

import java.util.concurrent.TimeUnit;

/**
也可以使用Phone phone = new Phone();Phone phone2 = new Phone();phone在线程A中调用发短信,phone2在线程B中调用打电话,这里锁的是两个不同的调用者,所以他们是两把锁,所以互不影响,因为发短信中有4秒的线程休眠,故而一定是打电话被先调用
 */
public class Test2 {
    public static void main(String[] args) {
        Phone2 phone = new Phone2();
        Phone2 phone1 = new Phone2();
        /**
         * 被synchronized 修饰的方式和普通方法 先执行sendSms() 还是 hello()
         * 答案: hello()
         *  解释:新增加的这个方法没有 synchronized 修饰,不是同步方法,不受锁的影响!
         */
        // 线程A调用phone发短信
        new Thread(()->{phone.sendSms();},"A").start();
        // 休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 线程B使用phone1调用打电话
        new Thread(()->{phone1.call();},"B").start();
    }
}

class Phone2{
    // 发短信
    public synchronized void sendSms(){
        // 休息四秒
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

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

第四锁

4、phone对象在线程A中调用被synchronized修饰的发短信方法 和 在线程B中调用未被synchronized修饰的普通方法hello

结果:hello先执行

import java.util.concurrent.TimeUnit;

        /**
         * 被synchronized 修饰的方法和普通方法 先执行sendSms() 还是 hello()
         * 答案: hello()
         *  解释:新增加的这个方法没有 synchronized 修饰,不是同步方法,不受锁的影响!
         */
public class Test2 {
    public static void main(String[] args) {
        Phone2 phone = new Phone2();
        // 线程A发短信
        new Thread(()->{phone.sendSms();},"A").start();
        // 休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 线程B调用未被synchronized修饰的方法hello()
        new Thread(()->{phone.hello();},"B").start();
    }
}

class Phone2{
    // 发短信
    public synchronized void sendSms(){
        // 休息四秒
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

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

    // say hello 没有被锁 也就是说不是同步方法、不受锁的影响,线程会直接把它来执行
    public void hello(){
        System.out.println("hello");
    }
}
第四锁解释
synchronized 修饰的方法和普通方法 先执行sendSms() 还是 hello()?
答案是hello()解释:新增加的这个方法没有 synchronized 修饰,不是同步方法,不受锁的影响!

第五锁

5、Phone3在线程A 和 线程B中分别调用Phone3的被synchronized和static修饰的静态同步方法 发短信 和 打电话。

结果:发信息先调用

import java.util.concurrent.TimeUnit;

/**
 * 将两个同步方法设置为静态
 */
public class Test3 {
    public static void main(String[] args) {
        // 线程A发短信
        new Thread(()->{Phone3.sendSms();},"A").start();

        // 休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 线程B打电话
        new Thread(()->{Phone3.call();},"B").start();
    }
}

class Phone3{

    /**
     * 在此处我们将 两个同步方法均设为静态方法,此两个方法在类加载是被加载
     * 所以,在此2个同步方法加载时,它们被全局唯一的Phone3.class对象加载,因为此Phone3.class全局唯一,故而他们被同一个对象加载
     * 所以此时仍然是发短信先输出
     */

    // 发短信
    public static synchronized void sendSms(){
        // 休息四秒
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    // 打电话
    public static synchronized void call(){
        System.out.println("打电话");
    }
}
第五锁解释
只要方法被 static 修饰,锁的对象就是 Class模板对象,这个则全局唯一!所以说这里是同一个锁,并不是因为synchronized  这里程序会从上往下依次执行

第六锁

6、phone、phone1两个对象来分别调用两个静态同步方法,哪个先执行?

结论:发短信先执行

import java.util.concurrent.TimeUnit;

/**
 */
public class Test3 {
    public static void main(String[] args) {
        // 因为sendSms和call方法均是静态方法,故而Phone3.sendSms()和new Phone3().sendSms()没有区别
        Phone3 phone = new Phone3();
        Phone3 phone1 = new Phone3();
        // 线程A发短信
        new Thread(()->{phone.sendSms();},"A").start();

        // 休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 线程B打电话
        new Thread(()->{phone1.call();},"B").start();
    }
}

class Phone3{

    // 发短信
    public static synchronized void sendSms(){
        // 休息四秒
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    // 打电话
    public static synchronized void call(){
        System.out.println("打电话");
    }
}
第六锁解释
/**
 * 同被static+synchronized 修饰的两个方法,是先发短信还是先打电话()?
 *  答案:发短信
 *  解释:只要方法被 static 修饰,锁的对象就是 Class模板对象,这个则全局唯一,class模板只有一个,即使是phone和phone1不是同以次实例化的对象,但是无论 phone.静态方法() 还是 phone1.静态方法() 它们均相当于Phone.静态方法()
 *  所以说这里是同一个锁,并不是因为synchronized导致。
 */

第七锁

7、被synchronized修饰的普通方法和被synchronized静态方法是先发短信还是先打电话?

结论:先打电话

import java.util.concurrent.TimeUnit;

/**
 */
public class Test3 {
    public static void main(String[] args) {
        // 因为sendSms和call方法均是静态方法,故而Phone3.sendSms()和new Phone3().sendSms()没有区别
        Phone3 phone = new Phone3();

        // 线程A发短信
        new Thread(()->{phone.sendSms();},"A").start();

        // 休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 线程B打电话
        new Thread(()->{phone.call();},"B").start();
    }
}

class Phone3{
    // 发短信
    public static synchronized void sendSms(){
        // 休息四秒
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    // 打电话
    public synchronized void call(){
        System.out.println("打电话");
    }
}
第七锁解释
/**
 * 被synchronized修饰的普通方法和被synchronized静态方法是先发短信还是先打电话?
 * 答案:打电话
 * 解释:只要被static修饰锁的是class模板, 而synchronized 锁的是调用的对象
 * 这里是两个锁互不影响,按时间先后执行
 */

第八锁

8、phone、phone1两个对象来分别调用被static+synchronized修饰的静态同步方法发短信 和 被synchronized修饰的普通方法打电话,哪个先执行?

结论:随机执行

import java.util.concurrent.TimeUnit;

/**
 */
public class Test3 {
    public static void main(String[] args) {
        // 因为sendSms和call方法均是静态方法,故而Phone3.sendSms()和new Phone3().sendSms()没有区别
        Phone3 phone = new Phone3();
        Phone3 phone1 = new Phone3();

        // 线程A发短信
        new Thread(()->{phone.sendSms();},"A").start();

        // 休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 线程B打电话
        new Thread(()->{phone1.call();},"B").start();
    }
}

class Phone3{
    // 发短信
    public static synchronized void sendSms(){
        // 休息四秒
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    // 打电话
    public synchronized void call(){
        System.out.println("打电话");
    }
}
第八锁解释
phone、phone1两个对象来分别调用被static+synchronized修饰的静态同步方法发短信 和 被synchronized修饰的普通方法打电话,先执行发短信还是打电话?
答案:打电话
解释: 只要被static 修饰的锁的就是整个class模板
这里一个锁的是class模板 一个锁的是调用者 
所以锁的是两个对象,互不影响,按CPU调度顺序执行
八锁总结
synchronized(Demo.class){
}synchronized(this){
}
1new this 调用的是这个对象,是一个具体的对象!
2static class 唯一的一个模板,即Class对象!

synchronized 锁的对象是方法的调用者!

普通同步方法的调用者是类的实例(实例化对象)
静态同步方法的调用者是类的对象(class对象)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值