mcxen / note

1 stars 0 forks source link

synchronized修饰静态方法和非静态方法区别文章保存到JUC里面文件夹 #17

Closed mcxen closed 2 months ago

mcxen commented 2 months ago

synchronized修饰静态方法和非静态方法区别

Synchronized修饰非静态方法,实际上是对调用该方法的对象加锁,俗称“对象锁”。

Synchronized修饰静态方法,实际上是对该类对象加锁,俗称“类锁”。

详细解答:

Java中每个对象都有一个锁,并且是唯一的。假设分配的一个对象空间,里面有多个方法,相当于空间里面有多个小房间,如果我们把所有的小房间都加锁,因为这个对象只有一把钥匙,因此同一时间只能有一个人打开一个小房间,然后用完了还回去,再由JVM 去分配下一个获得钥匙的人。

情况1:同一个对象在两个线程中分别访问该对象的两个同步方法

结果:会产生互斥。

解释:因为锁针对的是对象,当对象调用一个synchronized方法时,其他同步方法需要等待其执行结束并释放锁后才能执行。正如上面所解释的,相当于一个对象只有一把钥匙了,里面的两个同步方法是两个房间,因此,同一时间只能用一个钥匙访问一个方法。

情况2:不同对象在两个线程中调用同一个同步方法

结果:不会产生互斥。

解释:因为是两个对象,就相当于两个大房子,彼此之间互不干扰,具有两把钥匙。锁针对的是对象,并不是方法,所以可以并发执行,不会互斥。形象的来说就是因为我们每个线程在调用方法的时候都是new 一个对象,那么就会出现两个空间,两把钥匙。

情况1:用类直接在两个线程中调用两个不同的同步方法

结果:会产生互斥。

解释:因为对静态对象加锁实际上对类(.class)加锁,类对象只有一个,可以理解为任何时候都只有一个空间,里面有N个房间,一把锁,因此房间(同步方法)之间一定是互斥的。

注:上述情况和用单例模式声明一个对象来调用非静态方法的情况是一样的,因为永远就只有这一个对象。所以访问同步方法之间一定是互斥的。

情况2:用一个类的静态对象在两个线程中调用静态方法或非静态方法

结果:会产生互斥。

解释:因为是一个对象调用,同上。都调用静态方法的时候,相当于是同一个类锁,用的都是同一个类对象。

都调用非静态方法的时候,相当于是同一个对象锁。

情况3:一个对象在两个线程中分别调用一个静态同步方法和一个非静态同步方法

结果:不会产生互斥。

解释:因为虽然是一个对象调用,但是两个方法的锁类型不同,调用的静态方法实际上是类对象在调用,即这两个方法产生的并不是同一个对象锁,因此不会互斥,会并发执行。

例子

pulbic class Something(){
    public synchronized void isSyncA(){}
    public synchronized void isSyncB(){}
    public static synchronized void cSyncA(){}
    public static synchronized void cSyncB(){}
}

那么,加入有Something类的两个实例a与b,那么下列哪组方法可以被1个以上线程同时访问呢?

a. x.isSyncA()与 x.isSyncB() //同一个对象的普通方法,不可以访问,锁住了对象了
b. x.isSyncA()与 y.isSyncA() //不同对象的普通方法,可以访问
c. x.cSyncA()与 y.cSyncB()   // 不同对象静态方法锁住了,锁住了类,
d. x.isSyncA()与 Something.cSyncA()

这里,很清楚的可以判断:

a,都是对同一个实例的synchronized域访问,因此不能被同时访问

b,是针对不同实例的,因此可以同时被访问

c,在 c 选项中,x.cSyncA()y.cSyncB() 是对同一个类的静态方法进行访问,虽然是通过不同的实例调用,但静态同步方法的锁是类级别的(即 Something.class 这个类对象),而不是实例级别的。因此,cSyncA()cSyncB() 是针对同一个锁进行的同步访问。

由于 cSyncA()cSyncB() 都是静态同步方法,它们共享同一个类锁,因此这两个方法不能被不同线程同时访问,必须等一个方法释放了锁,另一个方法才能获得锁并执行。因此,c 选项的两个方法 不能 被同时访问。

d,书上的答案是可以被同时访问的,答案理由是synchronzied的是实例方法与synchronzied的类方法由于锁定(lock)不同的原因。

mcxen commented 2 months ago

OK