浅谈synchronized锁对象

我们知道,多个线程争抢同一个 monitor 的 lock 会陷入阻塞进而达到数据同步、资源同步的目的,而大家可能比较疑惑,这个monitor到底是啥?

this monitor

在下面的代码 ThisMonitor 中,两个方法 methodl 和 method2 都被 synchronized 关键字修饰,启动了两个线程分别访问 method1 和 method2。

public class ThisMonitor {
    public synchronized void method1() {
        System.out.println(Thread.currentThread().getName() + " enter to method1");
        try {
            TimeUnit.MINUTES.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public synchronized void method2() {
        System.out.println(Thread.currentThread().getName() + " enter to method2");
        try {
            TimeUnit.MINUTES.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        ThisMonitor thisMonitor = new ThisMonitor();
        new Thread(thisMonitor::method1, "T1").start();
        new Thread(thisMonitor::method2, "T2").start();
    }
}

在这里插入图片描述
我将重点的地方用红色的框标识了出来,T1 线程获取了<0x00000000d60ffe30> monitor 的 lock 并且处于休眠状态,而 T2 线程企图获取<0x00000000d60ffe30> monitor 的 lock 时陷入了 BLOCKED 状态,可见使用 synchronized 关键字同步类的不同实例方法, 争抢的是同一个 monitor 的 lock,而与之关联的引用则是 ThisMonitor 的实例引用。


public class ThisMonitor02 {
    private final Object MONITOR = new Object();
    public synchronized void method1() {
        System.out.println(Thread.currentThread().getName() + " enter to method1");
        try {
            TimeUnit.MINUTES.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void method2() {
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + " enter to method2");
            try {
                TimeUnit.MINUTES.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        ThisMonitor02 thisMonitor = new ThisMonitor02();
        new Thread(thisMonitor::method1, "T1").start();
        new Thread(thisMonitor::method2, "T2").start();
    }
}

现在我们可以确定,与之关联的引用则是ThisMonitor 的实例引用。其中,method1 保持方法同步的方式, method2 则采用了同步代码块的方式,并且使用的是 this 的 monitor,运行修改后的代码将会发现效果完全一样。查看文档可知:当线程调用一个同步方法时,它会自动获取该方法对象的内部锁,并在该方法返回时释放锁。即使返回是由未捕获的异常引起的,也会发生锁释放。

class monitor

public class ClassMonitor {
    public synchronized static void method1() {
        System.out.println(Thread.currentThread().getName() + " enter to method1");
        try {
            TimeUnit.MINUTES.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public synchronized static void method2() {
        System.out.println(Thread.currentThread().getName() + " enter to method1");
        try {
            TimeUnit.MINUTES.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        new Thread(ClassMonitor::method1, "T1").start();
        new Thread(ClassMonitor::method2, "T2").start();
    }
}

运行上面的代码,在同一时刻只能有一个线程访问 ClassMonitor 的静态方法,我们仍旧使用 jstack 命令分析其线程堆栈信息,如图所示。
在这里插入图片描述
将关键的地方用红色方框标识了出来,T1 线程持有<0x000000071118ed60> monitor 的锁在正在休眠,而 T2 线程在试图获取<0x000000071118ed60> monitor 锁的 时候陷入了 BLOCKED 状态,因此我们可以得出用 synchronized 同步某个类的不同静态方法争抢的也是同一个 monitor 的 lock,再仔细对比堆栈信息会发现与this monitor中关于 monitor 信 息 不 一 样 的 地 方 在 于 (a java.lang.Class for com.bjsxt.chapter05.demo05.ClassMonitor),由此可以推断与该 monitor 关联的引用是 ClassMonitord.class 实例。

public class ClassMonitor02 {
    public synchronized static void method1() {
        System.out.println(Thread.currentThread().getName() + " enter to method1");
        try {
            TimeUnit.MINUTES.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void method2() {
        synchronized (ClassMonitor02.class) {
            System.out.println(Thread.currentThread().getName() + " enter to method2");
            try {
                TimeUnit.MINUTES.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        new Thread(ClassMonitor02::method1, "T1").start();
        new Thread(ClassMonitor02::method2, "T2").start();
    }
}

其中静态方法 method1继续保持同步方法的方式,而 method2 则修改为同步代码块的方式,使用 ClassMonitor.class 的实例引用作为 monitor。查看文档可知:因为静态方法是与类而不是对象相关联。在这种情况下,线程获得与类关联的类对象的内在锁。

总结

看完上面的文章之后,我总结两点。第一,我们在使用this monitor锁对象的时候,在同一类里面,定义多个同步方法,一定要注意锁对象是谁,因为如果是同一个锁对象,那么你执行两个方法的时候,只会执行一个,另外一个不会执行。第二,我们在使用class monitor的时候,也要注意锁的类对象,如果在同一个类中,多个同步方法之间,如果是同一个锁,那么执行的时候,也不会同时执行。所以希望大家理解锁对象。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值