JUC并发编程学习(五)-8锁现象

简介: JUC并发编程学习(五)-8锁现象

以下名词解释:

顺序执行:先调用的先执行;

随机执行:没有规律,与计算机硬件资源有关,哪个线程先得到资源就先执行,各个线程之间互不干扰

将从以下8个方面介绍锁现象

1. 多个线程使用同一把锁-顺序执行

2. 多个线程使用同一把锁,其中某个线程里面还有阻塞-顺序先执行

3. 多个线程有锁与没锁-随机执行

4.多个线程使用多把锁-随机执行

5. Class锁:多个线程使用一个对象-顺序执行

6. Class锁:多个线程使用多个对象-顺序执行

7. Class锁与对象锁:多个线程使用一个对象-随机执行

8. Class锁与对象锁:多个线程使用多个对象-随机执行

1. 多个线程使用同一把锁-顺序执行

多个线程使用同一个对象,多个线程就是使用一把锁,先调用的先执行!

示例1、 标准访问,请问先打印邮件还是短信?

package com.juc.study.lock8;
/**
* @ClassName:
* @PackageName: com.juc.study.lock8
* @author: youjp
* @create: 2020-04-15 16:32
* @description:    TODO 示例1.标准访问,请问先打印邮件还是短信?
* @Version: 1.0
*/
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
        new Thread(() -> {
            phone.sendMsg();
        }, "B").start();
    }
}
class Phone {
    public synchronized void sendEmail() {
        System.out.println("发邮件");
    }
    public synchronized void sendMsg() {
        System.out.println("发短信");
    }
}

20200401134307494.png

2. 多个线程使用同一把锁,其中某个线程里面还有阻塞-顺序先执行

多个线程使用同一个对象,多个线程就是使用一把锁,先调用的先执行,即使在某方法中设置了阻塞。

示例2、邮件方法暂停4秒钟,请问先打印邮件还是短信?

package com.juc.study.lock8;
import java.util.concurrent.TimeUnit;
/**
* @ClassName:
* @PackageName: com.juc.study.lock8
* @author: youjp
* @create: 2020-04-15 17:02
* @description:    TODO 示例2、邮件方法暂停4秒钟,请问先打印邮件还是短信?
* @Version: 1.0
*/
public class Test2 {
    public static void main(String[] args) {
        Phone2 phone=new Phone2();
        new Thread(() -> {
            phone.sendEmail();
        }, "A").start();
        new Thread(() -> {
            phone.sendMsg();
        }, "B").start();
    }
}
class Phone2 {
    public  synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发邮件");
    }
    public synchronized void sendMsg() {
        System.out.println("发短信");
    }
}

20200401134307494.png

3. 多个线程有锁与没锁-随机执行

多个线程,有的线程有锁,有的线程没锁,两者之间不存在竞争同一把锁的情况,先后执行顺序是随机的。

这种情况犹如你跟你老婆下班回家,家里面的厕所是有锁的,卧室没有锁,俩人到家后,你老婆先上厕所(有锁),你可以一直等待你老婆出来,你再去厕所后,才进卧室,你也可以先进卧室,等你老婆出来后,你再进厕所。

示例3、新增一个普通方法发qq,请问先打印邮件还是接收QQ?

package com.juc.study.lock8;
import java.util.concurrent.TimeUnit;
/**
* @ClassName:
* @PackageName: com.juc.study.lock8
* @author: youjp
* @create: 2020-04-15 17:02
* @description:    TODO 示例3、新增一个普通方法发qq,请问先打印邮件还是接收QQ?
* @Version: 1.0
*/
public class Test3 {
    public static void main(String[] args) {
        Phone3 phone=new Phone3();
        new Thread(() -> {
            phone.sendEmail();  //发邮件
        }, "A").start();
        new Thread(() -> {
            phone.sendMsg();    //发短信
        }, "B").start();
        new Thread(() -> {
            phone.sendQQ(); //发QQ
        }, "C").start();
    }
}
class Phone3 {
    //发邮件
    public  synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发邮件");
    }
    //发短信
    public synchronized void sendMsg() {
        System.out.println("发短信");
    }
    //发QQ:  新增的方法没有被 synchronized 修饰,不是同步方法,所以不需要等待,其他线程用了一个把锁
    public void sendQQ(){
        System.out.println("发QQ");
    }
}

20200401134307494.png

4.多个线程使用多把锁-随机执行

1、被 synchronized 修饰的方法,锁的对象是方法的调用者;

2、调用者不同,它们之间用的不是同一个锁,相互之间没有关系。

示例4、两部手机、请问先打印邮件还是短信?

package com.juc.study.lock8;
import java.util.concurrent.TimeUnit;
/**
* @ClassName:
* @PackageName: com.juc.study.lock8
* @author: youjp
* @create: 2020-04-15 17:18
* @description:    TODO 示例4、两部手机、请问先打印邮件还是短信?
* @Version: 1.0
*/
public class Test4 {
    public static void main(String[] args) {
        Phone4 phoneOne=new Phone4();
        Phone4 phoneTwo=new Phone4();
        // 两个线程使用的是同一个对象。两个线程是一把锁!先调用的先执行!
        new Thread(() -> {
            phoneOne.sendEmail();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
          phoneTwo.sendMsg();
        }, "B").start();
    }
}
class Phone4 {
    /**
     *  @description:
     *  被 synchronized 修饰的方法,锁的对象是方法的调用者;
     *  调用者不同,它们之间用的不是同一个锁,相互之间没有关系。
     */
    //发邮件
    public  synchronized void sendEmail() {
        //善意延迟
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"发邮件");
    }
    //发短信
    public synchronized void sendMsg() {
        System.out.println(Thread.currentThread().getName()+"发短信");
    }
}

20200401134307494.png

5. Class锁:多个线程使用一个对象-顺序执行

被 synchronized 和 static 同时修饰的方法,锁的对象是类的 class 对象,是唯一的一把锁。线程之间是顺序执行。

锁Class和锁对象的区别:

1、Class 锁 ,类模版,只有一个;

2、对象锁 , 通过类模板可以new 多个对象。

如果全部都锁了Class,那么这个类下的所有对象都具有同一把锁

示例5、两个静态同步方法,同一部手机,请问先打印邮件还是短信?

package com.juc.study.lock8;
import java.util.concurrent.TimeUnit;
/**
* @ClassName:
* @PackageName: com.juc.study.lock8
* @author: youjp
* @create: 2020-04-15 17:18
* @description:    TODO 示例4、两个静态同步方法,同一部手机,请问先打印邮件还是短信?*
* @Version: 1.0
*/
public class Test5 {
    public static void main(String[] args) {
        Phone5 phoneOne=new Phone5();
        new Thread(() -> {
            phoneOne.sendEmail();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
          phoneOne.sendMsg();
        }, "B").start();
    }
}
class Phone5 {
    /**
     *  @description:
     *锁Class和锁对象的区别:
     * 1、锁 Class,类模版,只有一个;
     * 2、锁  对象,通过 类模板可以new 多个对象.
     * 如果全部都锁了Class,那么这个类下的所有对象都具有同一把锁。
     *被 synchronized 修饰 和 static 修饰的方法,锁的对象是类的 class 对象,是唯一的一把锁。线程之间是顺序执行。
     */
    //发邮件
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"发邮件");
    }
    //发短信
    public static synchronized void sendMsg() {
        System.out.println(Thread.currentThread().getName()+"发短信");
    }
}

6. Class锁:多个线程使用多个对象-顺序执行

被 synchronized 修饰 和 static 修饰的方法,锁的对象是类的 class 对象,是唯一的一把锁。

Class锁是唯一的,所以多个对象使用的也是同一个Class锁。

示例6、两个静态同步方法,2部手机,请问先打印邮件还是短信?

package com.juc.study.lock8;
import java.util.concurrent.TimeUnit;
/**
* @ClassName:
* @PackageName: com.juc.study.lock8
* @author: youjp
* @create: 2020-04-15 17:18
* @description: 示例6、两个静态同步方法,2部手机,请问先打印邮件还是短信?
* @Version: 1.0
*/
public class Test6 {
    public static void main(String[] args) {
        Phone6 phone1 = new Phone6();
        Phone6 phone2 = new Phone6();
        new Thread(() -> {
            phone1.sendEmail();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone2.sendMsg();
        }, "B").start();
    }
}
class Phone6 {
    /**
     * @description: 锁Class和锁对象的区别:
     * 1、锁 Class,类模版,只有一个;
     * 2、锁  对象,通过 类模板可以new 多个对象.
     * 如果全部都锁了Class,那么这个类下的所有对象都具有同一把锁。
     * 被 synchronized和static修饰的方法,锁的对象是类的class对象!唯一的同一把锁
     */
    //发邮件
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "发邮件");
    }
    //发短信
    public static synchronized void sendMsg() {
        System.out.println(Thread.currentThread().getName() + "发短信");
    }
}

20200401134307494.png

7. Class锁与对象锁:多个线程使用一个对象-随机执行

被 synchronized和static修饰的方法,锁的对象是类的class对象!唯一的同一把锁;

只被synchronized修饰的方法,是普通锁(如对象锁),不是Class锁,所以进程之间执行顺序互不干扰。

示例7、一个普通同步方法,一个静态同步方法,同一部手机,请问先打印邮件还是短信?

package com.juc.study.lock8;
import java.util.concurrent.TimeUnit;
/**
* @ClassName:
* @PackageName: com.juc.study.lock8
* @author: youjp
* @create: 2020-04-15 17:18
* @description: 示例7、一个普通同步方法,一个静态同步方法,同一部手机,请问先打印邮件还是短信?
* @Version: 1.0
*/
public class Test7 {
    public static void main(String[] args) {
        Phone7 phone1 = new Phone7();
        new Thread(() -> {
            phone1.sendEmail();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone1.sendMsg();
        }, "B").start();
    }
}
class Phone7 {
    //发邮件 :class 锁
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "发邮件");
    }
    //发短信:对象锁
    public  synchronized void sendMsg() {
        System.out.println(Thread.currentThread().getName() + "发短信");
    }
}

20200401134307494.png

8. Class锁与对象锁:多个线程使用多个对象-随机执行

被 synchronized和static修饰的方法,锁的对象是类的class对象!唯一的同一把锁;

只被synchronized修饰的方法,是普通锁(如对象锁),不是Class锁,所以进程之间执行顺序互不干扰。

示例8、一个普通同步方法,一个静态同步方法,2部手机,请问先打印邮件还是短信?

package com.juc.study.lock8;
import java.util.concurrent.TimeUnit;
/**
* @ClassName:
* @PackageName: com.juc.study.lock8
* @author: youjp
* @create: 2020-04-16 10:19
* @description:    示例8、一个普通同步方法,一个静态同步方法,2部手机,请问先打印邮件还是短信?
*
* @Version: 1.0
*/
public class Test8 {
    public static void main(String[] args) {
        Phone8 phone1 = new Phone8();
        Phone8 phone2 = new Phone8();
        new Thread(() -> {
            phone1.sendEmail();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone2.sendMsg();
        }, "B").start();
    }
}
class Phone8 {
    //发邮件 :class 锁
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "发邮件");
    }
    //发短信:对象锁
    public  synchronized void sendMsg() {
        System.out.println(Thread.currentThread().getName() + "发短信");
    }
}

20200401134307494.png

小结

new this 本身的这个对象,调用者

static class 类模板,保证唯一!

一个对象中有多个 synchronized方法,某个时刻内只要有一个线程去访问 synchronized 方法了就会被加锁,独立公共厕所!其他线程就会阻塞!

加了一个普通方法后两个对象,无关先后,一个有锁,一个没锁!情况会变化!

换成静态同步方法,情况会变化! CLASS ,所有静态同步方法的锁唯一 ,对象实例class 本身!

有兴趣的老爷,可以关注我的公众号【一起收破烂】,回复【006】获取2021最新java面试资料以及简历模型120套哦~

相关文章
|
4月前
|
安全 Java 调度
Java多线程基础-14:并发编程中常见的锁策略(二)
这段内容介绍了互斥锁和读写锁的概念以及它们在多线程环境中的应用。互斥锁仅允许进入和退出代码块时加锁和解锁,而读写锁则区分读和写操作,允许多个线程同时读但写时互斥。
42 0
|
4月前
|
算法 安全 Java
Java多线程基础-14:并发编程中常见的锁策略(一)
乐观锁和悲观锁是并发控制的两种策略。悲观锁假设数据容易产生冲突,因此在读取时即加锁,防止其他线程修改,可能导致效率较低。
40 0
|
4月前
|
Java
高并发编程之JUC 三大辅助类和读写锁
高并发编程之JUC 三大辅助类和读写锁
44 1
|
4月前
|
安全 编译器 Linux
多线程(进阶一:锁策略)
多线程(进阶一:锁策略)
154 0
|
11月前
|
存储 缓存 算法
Java多线程与并发-原理
Java多线程与并发-原理
46 0
|
11月前
|
设计模式 安全 Java
JUC第十二讲:JUC锁 - 看不懂锁核心类 AQS 原理来打我
JUC第十二讲:JUC锁 - 看不懂锁核心类 AQS 原理来打我
|
存储 Java
【一文读懂】 Java并发 - 锁升级原理
Java对象头,锁升级的原因,重量级锁、轻量级锁、偏向锁的原理
251 0
【一文读懂】 Java并发 - 锁升级原理
|
算法 Java
JUC并发编程学习(二)-进程和线程知识回顾
JUC并发编程学习(二)-进程和线程知识回顾
JUC并发编程学习(二)-进程和线程知识回顾
|
数据可视化 Java
每日面试:经典死锁问题 | 如何解决死锁问题 | 多线程
每日面试:经典死锁问题 | 如何解决死锁问题 | 多线程
168 0
JUC并发编程学习(九)-读写锁
JUC并发编程学习(九)-读写锁
JUC并发编程学习(九)-读写锁