带你真的理解synchronize的对象锁和类锁的使用

先来简单理解一下对象锁和类锁:

Java对象锁

对象锁是用于对象实例方法,或者一个对象实例上的

有一个类A,A里面有一些方法或者代码块使用了对象锁。如:

class A{
    //同步方法
    public void test1() {
        synchronized (this) {
            
        }
    }
    //跟上面的test1方法一样都是对象锁,只是锁的尺寸不同
    public synchronized void test2() {
       
    }
    //方法二
    public void test3() {
        
    }
}

现在有线程1new了一个A的对象,对象为a,并调用了使用对象锁方法test1(),即:

A a = new A();
a.test1();
---到此是线程1执行的代码---

此时当线程1在执行加了对象锁的同步方法test1的那一刻,这时又有一个线程2过来,想要执行下面这段代码会怎么样呢?

a.test2();
---到此是线程2执行的代码---

如果是正常的多线程场景,同一时刻,线程1执行a.test1()和线程2执行a.test2()是互不影响的。

但是这里的线程2必须等线程1释放对象锁之后才可以执行test2(),因为:

test1()方法加了synchronized对象锁,那在线程1执行对象a的test1()方法时,就会锁住对象实例a的内存,在线程1还没释放对象锁之前,任何线程都是没办法进入对象a里面的。而线程1只会在执行完同步方法或者同步代码块只会才会释放对象锁。

但,如果线程2做的是这么一个操作:

a.test3();

这个时候线程3执行test2方法就不需要等待线程1,进行了同步的方法(加锁方法)和没有进行同步的方法(普通方法)是互不影响的,一个线程进入了同步方法,得到了对象锁,其他线程还是可以访问那些没有同步的方法(普通方法)。这里涉及到内置锁的一个概念(此概念出自java并发编程实战第二章):

对象的内置锁和对象的状态之间是没有内在的关联的,虽然大多数类都将内置锁用做一种有效的加锁机制,但对象的域并不一定通过内置锁来保护。当获取到与对象关联的内置锁时,并不能阻止其他线程访问该对象,当某个线程获得对象的锁之后,只能阻止其他线程获得同一个锁。之所以每个对象都有一个内置锁,是为了免去显式地创建锁对象。

在微服务的后端中,每个不同的请求都会新建一个对象来handle,理论上对象锁是不会造成我们想要的约束的

Java类锁

类锁是用于类的静态方法或者一个类的class对象上的

同样的例子,有一个类A,只是A里面使用synchronize的方法不一样了,这种使用方法会锁住类A。如:

class A{
    //同步方法
    public void test1() {
        synchronized (A.class) {
            
        }
    }
    //跟上面的同步方法一样,都是类锁,只是表现形式不同
    public static synchronized void test2() {
        
    }
    public synchronized void test3() {
        
    }
}

现在有线程1new了一个A的对象,对象为a,并调用了使用对象锁方法test1(),即:

A a = new A();
a.test1();
---到此是线程1执行的代码---

此时当线程1在执行加了对象锁的同步方法test1的那一刻,这时又有一个线程2过来,想要执行下面这段代码会怎么样呢?

A.test2();
---到此是线程2执行的代码---

这里的线程2必须等线程1释放类锁之后才可以执行A.test2(),因为:

线程1执行的test1()方法里面锁住了A.class,也就是锁住了类A的内存。那同一时刻所有实例想使用test1方法或者是其他加了类锁的形如A.test2()方法,都得等线程1释放类锁后才能执行。线程2要用加了类锁的静态方法test2,那自然是用不了了。

想一下,如果是在高并发的情况下,所有要执行类A的test1请求都得阻塞等待最先进入类A的类锁的线程执行完,释放类锁后,才能执行。如果这个锁住的方法执行的时候过长,或者直接死循环,那整个系统将会被拖垮甚至瘫痪。

但,如果线程2做的是这么一个操作:

a.test3();

结果是线程1执行了类锁的同步方法,线程2执行了对象锁的同步方法,两者互不影响。这证明了类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。

在微服务的后端中,我们常用类锁来进行约束

其实总结起来很简单:

  • 一个锁的是类对象,一个锁的是实例对象。
  • 若类对象被lock,则类对象的所有同步方法全被lock;
  • 若实例对象被lock,则该实例对象的所有同步方法全被lock
到这里大家应该对对象锁和类锁应该有个更加清晰的理解了

注意:

上面我们有讲到synchronize的一个缺陷,就是如果同步的方法死循环了或者执行时间过长了,会影响系统的正常运行。那么为了将这种风险降到最低,一般我们使用类锁的时候,不会直接synchronize(xx.class)或者static synchronized去锁住这个类的所有同步方法,而是用先在这个类里面声明了一个对象实例:Object object=new Object(),然后再synchronize(object)。那么这个方法加锁的对象是object这个对象,当一个线程执行这个方法时,这对其他线程要执行这个类的其他同步方法是没有影响的,只会影响到要执行用synchronize(object)锁住的方法,因为他们持有的锁都完全不一样。


总结五种用法:

一、this

synchronized(this){ 
//互斥代码
}

这里的this指的是执行这段代码的对象,synchronized得到的锁就是this这个对象的锁

public synchronized void func(){ 
//互斥代码
}

二、A.class

synchronized(A.class){ 
//互斥代码
}

这里A.class得到的是A这类,所以synchronized关键字得到的锁是类的锁,这种方法同下面的方法功能是相同的:

public static synchronized void fun(){ 
//互斥代码
}

所有需要类的锁的方法都不能同时执行,但是它和需要某个对象的锁的方法或者是不需要任何锁的方法可以同时执行。

三、object.getClass()

synchronized(object.getClass){ 
//互斥代码
}

这种方法一般情况下同第二种是相同,但是出现继承和多态时,得到的结果却是不相同的。所以一般情况下推荐使用A.class的方式。

四、object

private Object lock = new Object();
public void test1(){
    synchronized(lock){ 
    //互斥代码
    }
}

这里synchronized关键字拿到的锁是对象object的锁,所有需要这个对象的锁的方法都不能同时执行。这是最常用的高并发场景下要锁住某个方法所用的操作。

五、static object

上边的代码稍作修改就可以起到互斥作用,将类中Object对象的声明改为下面这样:

private static Object lock = new Object();

这样不同的类使用的就是同一个object对象,需要的锁也是同一个锁,就可以达到互斥的效果了。

码字不易,转发请附带出处。如果喜欢我的文章,可以点赞关注,多多支持我哦!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容