JUC并发编程——8锁现象(基于狂神说的学习笔记)

8锁现象

深刻理解锁究竟是什么,锁的对象究竟是谁

8个锁的问题,两两分组,问题,答案,代码皆在下面代码块中,如有问题后续补充

package Lock8;

import java.util.concurrent.TimeUnit;

/**
 * 8锁,就是关于锁的8个问题:
 * 1、标准情况下,两个线程先打印哪一个?---->先打印sendSms
 * 2、sendSms延迟4秒后,两个线程哪个线程先打印?---->先打印sendSms,因为synchronized锁锁的是资源对象
 */
public class Test01 {

    public static void main(String[] args) {
        // 资源类
        Phone phone = new Phone();
        // 创建并开启线程
        new Thread(()->{
            phone.sendSms();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

class Phone{

    // synchronized 锁的对象是方法的调用者,因此,资源对象被锁住了,线程A与线程B所使用的资源对象为同一个phone
    // 两个方法用的是同一个锁,谁先拿到谁执行
    public synchronized void sendSms(){
//--------------------------- 2 --------------------------
        // 让sendSms加4秒延迟
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
//--------------------------- 2 --------------------------
        System.out.println("send a sms");
    }

    public synchronized void call(){
        System.out.println("call");
    }
}
package Lock8;

import java.util.concurrent.TimeUnit;

/**
 * 3、增加了一个普通方法后,是先执行发短信还是执行hello--->hello 先输出,因为hello并不是同步方法,不受锁的影响,而sendSms有睡眠延迟
 * 4、两个对象,两个同步方法,先执行发短信还是执行打电话?----> call 先输出,这里有两个不同的对象,也就意味着有两把锁,A与B拿的锁不一样
 */
public class Test02 {

    public static void main(String[] args) {
// ------------------------- 3 -----------------------------------
//        // 资源类
//        Phone2 phone = new Phone2();
//        // 创建并开启线程
//        new Thread(()->{
//            phone.sendSms();
//        },"A").start();
//
//        try {
//            TimeUnit.SECONDS.sleep(1);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
//        new Thread(()->{
//            phone.hello();
//        },"B").start();
// ------------------------- 3 -----------------------------------
// ------------------------- 4 -----------------------------------
        // 资源类
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        // 创建并开启线程
        new Thread(()->{
            phone1.sendSms();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
// ------------------------- 4 -----------------------------------

    }
}

class Phone2{

    // synchronized 锁的对象是方法的调用者,因此,资源对象被锁住了,线程A与线程B所使用的资源对象为同一个phone
    // 两个方法用的是同一个锁,谁先拿到谁执行
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("send a sms");
    }

    public synchronized void call(){
        System.out.println("call");
    }

    // 这里没有锁,不是同步方法,不受锁的影响
    public void hello(){
        System.out.println("this function is saying hello!");
    }
}
package Lock8;

import java.util.concurrent.TimeUnit;

/**
 *5、增加两个静态的同步方法,只有一个对象,先打印哪一个?----->
 * 先发短信,因为当将方法声明为static时,则该方法全局唯一,与Class一同加载,换句话说,该锁锁的是类,而非方法
 *6、两个对象,增加两个静态的同步方法,先打印哪一个?----->
 * 先发短信,同样的,因为static的缘故,synchronized锁住的是类,而类只有一个,因此实际上只有一把锁
 */
public class Test03 {

    public static void main(String[] args) {
// ------------------------- 5 -----------------------------------
//        // 资源类
//        Phone3 phone = new Phone3();
//
//        // 创建并开启线程
//        new Thread(()->{
//            phone.sendSms();
//        },"A").start();
//
//        try {
//            TimeUnit.SECONDS.sleep(1);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
//        new Thread(()->{
//            phone.call();
//        },"B").start();
// ------------------------- 5 -----------------------------------
// ------------------------- 6 -----------------------------------
        // 资源类
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();

        // 创建并开启线程
        new Thread(()->{
            phone1.sendSms();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
// ------------------------- 6 -----------------------------------

    }
}

// Phone3 唯一的一个class对象
// 在Java的世界里,万物皆对象,类也不例外,类有两个对象,实例对象以及Class对象
// Class对象包含了与类有关的信息,实例对象就是通过Class对象创建的
class Phone3{

    // synchronized 锁的对象是方法的调用者
    // static 静态方法
    // 类一加载就有了,锁的是Class
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("send a sms");
    }

    public static synchronized void call(){
        System.out.println("call");
    }
}
package Lock8;

import java.util.concurrent.TimeUnit;

/**
 *7、 一个静态同步方法,一个普通同步方法,一个对象,谁先打印?----->
 * 打电话先打印,发短信锁的是Class类模板,打电话锁的是对象,锁的对象不一样,因此有两把锁
 *8、 一个静态同步方法,一个普通同步方法,两个对象,谁先打印?----->
 * 打电话先打印,发短信锁的是Class类模板,打电话锁的是对象,锁的对象不一样,因此有两把锁
 */
public class Test04 {

    public static void main(String[] args) {

// ------------------------- 7 -----------------------------------
//        // 资源类
//        Phone4 phone = new Phone4();
//
//        // 创建并开启线程
//        new Thread(()->{
//            phone.sendSms();
//        },"A").start();
//
//        try {
//            TimeUnit.SECONDS.sleep(1);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
//        new Thread(()->{
//            phone.call();
//        },"B").start();
// ------------------------- 7 -----------------------------------
// ------------------------- 8 -----------------------------------
        // 资源类
        Phone4 phone1= new Phone4();
        Phone4 phone2 = new Phone4();

        // 创建并开启线程
        new Thread(()->{
            phone1.sendSms();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
// ------------------------- 8 -----------------------------------

    }
}

class Phone4{
    // 静态同步方法  锁的是Class类模板
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("send a sms");
    }

    // 普通同步方法  锁的是调用者
    public synchronized void call(){
        System.out.println("call");
    }
}

小结

new 出来的对象,是一个实例对象,实例可以有很多个,因此synchronized锁只是锁在实例对象上,如果两个线程持有的实例对象资源不同,则两个线程独立,并不会被对方的锁干扰

static 则表名该方法与Class同步产生,因此,static是在Class对象中的,如果synchronized锁锁住了static方法,则表明锁住了整个Class对象,而因为Class对象唯一(所有的实例对象都通过Class对象创建的)因此,无论后续创建了多少个实例对象资源,最终只要某一线程持有住某一静态同步方法的锁,则其他静态同步方法一律无法使用。

而同时,因为Class对象和实例对象本质上是两个对象(虽然实例对象是通过Class对象创建而来的),因此某一线程持有某一静态同步方法的锁,也无法影响其他普通同步方法

总而言之:

普通同步锁实例,静态同步锁模板,

即使再多实例在,静态之间统一管

静态普通不同属,相互之间无法管

你可能感兴趣的:(JUC并发编程,学习,笔记,python)