synchronized - 八锁问题及扩展

通过8锁问题,可以大致清楚synchronized是锁的什么东西。通过对8锁问题的扩展,可以进一步理解synchronized。

8锁问题

1、一个对象,两个synchronized方法,一个方法睡眠
public class LockTest {
    public static void main(String[] args) {
        Phone phone = new Phone();  // 一个Phone对象
        new Thread(() -> {
            phone.sendSms();
        },"A").start();
        new Thread(() -> {
            phone.call();
        },"B").start();
    }
}

class Phone{
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(1);  // 休眠1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
}

执行结果:1、发短信 2、打电话
原因:两个方法都被synchronized锁了,实际也就是锁的phone这个对象。A线程先拿到锁,sleep并不会释放锁。因此,只有当A线程执行完毕释放了锁,B拿到锁才能执行。

2、一对象,两synchronized方法,main方法里睡眠
public class LockTest {
    public static void main(String[] args) {
        Phone phone = new Phone();  // 一个Phone对象
        new Thread(() -> {
            phone.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);  // 休眠1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone.call();
        },"B").start();
    }
}

class Phone{
    public synchronized void sendSms(){
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
}

执行结果:1、发短信 2、打电话
原因:与第1中情况一样,锁的都是同一个对象,只有先拿到锁的才会先执行。

3、一对象,一synchronized方法,一普通方法
public class LockTest {
    public static void main(String[] args) {
        Phone phone = new Phone();  // 一个Phone对象
        new Thread(() -> {
            phone.sendSms();
        },"A").start();
        new Thread(() -> {
            phone.qq();
        },"B").start();
    }
}

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

    public void qq(){
        System.out.println("聊QQ");
    }
}

执行结果:1、聊QQ 2、发短信
原因:普通方法不受锁的影响,被锁住的方法休眠了2秒,但普通方法不受锁的影响,所以它会先执行。

4、两对象,两synchronized方法
public class LockTest {
    public static void main(String[] args) {
        Phone phone1 = new Phone();  // phone1
        Phone phone2 = new Phone();  // phone2
        new Thread(() -> {
            phone1.sendSms();
        },"A").start();
        new Thread(() -> {
            phone2.call();
        },"B").start();
    }
}

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

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

执行结果:1、打电话 2、发短信
原因:调用两个方法的是两个不同的对象,synchronized锁的也是不同对象,它们互不影响。

5、一个对象,两个静态synchronized方法
public class LockTest {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendSms();
        },"A").start();
        new Thread(() -> {
            phone.call();
        },"B").start();
    }
}

class Phone{
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(2);  // 休眠2秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    
    public static synchronized void call(){
        System.out.println("打电话");
    }
}

执行结果:1、发短信 2、打电话
原因:静态方法属于Class,Class对象全局唯一,一个类只有一个Class对象,因此这个synchronized锁的是Class。所以还是谁先拿到锁,谁先执行。

6、两对象,两synchronized静态方法
public class LockTest {
    public static void main(String[] args) {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(() -> {
            phone1.sendSms();
        },"A").start();
        new Thread(() -> {
            phone2.call();
        },"B").start();
    }
}

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

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

执行结果:1、发短信 2、打电话
原因:再次印证,静态方法锁的是Class。

7、一对象,一静态synchronized方法,一普通synchronized方法
public class LockTest {
    public static void main(String[] args) {
        Phone1 phone = new Phone();
        new Thread(() -> {
            phone1.sendSms();
        },"A").start();
        new Thread(() -> {
            phone1.call();
        },"B").start();
    }
}

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

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

}

执行结果:1、打电话 2、发短信
原因:sendSms方法的synchronized是锁的Class对象,而call方法的synchronized是锁的phone1对象。因此两个方法在上诉代码中的调用,各自持有一把锁,互不影响。

8、两对象,一静态synchronized方法,一普通synchronized方法
public class LockTest2 {
    public static void main(String[] args) {
        Phone1 phone1 = new Phone1();
        Phone1 phone2 = new Phone1();
        new Thread(() -> {
            phone1.sendSms();
        },"A").start();
        new Thread(() -> {
            phone2.call();
        },"B").start();
    }
}

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

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

}

执行结果:1、打电话 2、发短信
原因:再次印证static的synchronized锁的是Class对象

8锁结论

一个对象只有一把锁,加锁的普通方法,谁先抢到锁,谁先执行。
静态的加锁方法,锁的是Class对象,Class对象全局唯一。

synchronized的扩充问题

1、当被锁的对象发生变化的时候
public class Synchronized1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.changed();
        },"A线程").start();

        try{
            TimeUnit.SECONDS.sleep(2);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        Thread thread = new Thread(() -> {
           phone.changed();
        },"B线程");
        System.out.println(phone.object);
        // object对象发生了变化
        phone.object = new Object();
        System.out.println(phone.object);
        // 如果锁的对象发生改变,则相当于锁的是一个新的对象了
        thread.start();
    }
}

class Phone{
    Object object = new Object();

    public void changed(){
        // 锁的是object对象
        synchronized (object){
            while (true){
                try{
                    TimeUnit.SECONDS.sleep(2);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
            }
        }
    }

}

在这里插入图片描述

当被锁的对象发生改变,则表示又锁了一个新的对象,应该避免将锁定对象的引用改变。如果只是改变对象的属性,则不会有影响。

2、以字符串常量作为锁定对象的时候
public class Synchronized1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.helloWorld();
        },"A").start();
        new Thread(() -> {
            phone.worldHello();
        },"B").start();
    }
}

class Phone{
    // 两个字符串引用都是指向常量池中的同一个字符串
    String str1 = "hello";
    String str2 = "hello";

    public void helloWorld(){
        synchronized (str1){
            System.out.println("str1 运行!");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("str1 2秒后结束!");
        }
    }

    public void worldHello(){
        synchronized (str2){
            System.out.println("str2 运行!");
        }
    }
}

在这里插入图片描述
因为str1和str2都是指向字符串常量池中的同一个字符串,所以不管锁的是str1还是str2,都是锁的同一个对象。

3、synchronized方法里调用synchronized方法
public class Synchronized1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.one();
    }
}

class Phone{
    synchronized void one(){
        System.out.println("one func start...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        two();
    }

    synchronized void two(){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("two func start...");
    }
}

在这里插入图片描述
可以调用到,因为synchronized是可重入锁。

4、继承情况下的重入锁
public class Synchronized1 {
    public static void main(String[] args) {
        MobilePhone mobilePhone = new MobilePhone();
        mobilePhone.phone();
    }
}

class Phone{
    synchronized void phone(){
        System.out.println("Phone Start...");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Phone End...");
    }
}

class MobilePhone extends Phone{
    @Override
    synchronized void phone() {
        System.out.println("MobilePhone Start...");
        super.phone();
        System.out.println("MobilePhone End...");
    }
}

在这里插入图片描述

5、异常情况下的synchronized
public class Synchronized1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.test();
        },"A线程").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone.test();
        },"B线程").start();
    }
}

class Phone{
    int count = 0;

    synchronized void test(){
        System.out.println(Thread.currentThread().getName() + " Start...");
        while(true){
            count++;
            System.out.println(Thread.currentThread().getName() + "的count = " + count);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(count == 5){
                try {
                    // 如果这里的异常没有被捕获处理,则会释放锁
                    int i = 1 / 0;
                }catch (Exception e){
                    System.out.println("异常处理!");
                }
            }
        }
    }
}

在这里插入图片描述
如果synchronized修饰的块里有地方出现异常,如果不去捕获处理这个异常,synchronized锁会被主动释放掉。如果捕获处理这个异常之后,这个锁就不会主动释放。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值