12

这是我在链接上找到的一段文字。

"避免锁定静态方法

最糟糕的解决方案是将“同步”关键字放在静态方法上,这意味着它将锁定该类的所有实例。”

为什么同步静态方法会锁定类的所有实例?它不应该只是锁定班级吗?

4

6 回答 6

33

要理解这一点,最简单的方法是比较 lock 与实例方法和静态方法的工作方式。假设您有一个类 Test.java,它有如下两个方法。

public class Test{
   public synchronized void instanceMethod(){
   }

   public synchronized static void staticMethod(){
   } 
}

同时,有两个Test类实例,testA和testB。还有两个线程 tA 和 tB 试图并行访问类 Test。

锁定 instanceMethod:当 tA 获得 testA 的 instanceMethod 锁定时,tB无法访问testA中的相同方法,但 tB 仍然可以自由调用testB中的instanceMethod。因为针对instanceMethod 的同步是实例级锁定

锁定 staticMethod:但是,当 tA 获得staticMethod上的锁定时,锁定与 testA 或 testB 无关,因为 static 方法上的同步是类级别的锁定。这意味着 tB 根本无法访问staticMethod直到 tA 释放锁

于 2013-03-11T01:16:35.410 回答
8

实际上,锁定 class 的静态方法与Foo锁定Foo.class(这是唯一的实例)相同:

public static void doSomething()
{
    synchronized(Foo.class)
    {
        // ...
    }
}
于 2013-03-11T01:14:39.463 回答
7

这是我的测试代码,表明您是对的,并且文章有点过于谨慎:

class Y {
    static synchronized void staticSleep() {
        System.out.println("Start static sleep");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
        }
        System.out.println("End static sleep");
    }

    synchronized void instanceSleep() {
        System.out.println("Start instance sleep");
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
        }
        System.out.println("End instance sleep");
    }
}

public class X {
    public static void main(String[] args) {
        for (int i = 0; i < 2; ++i) {
            new Thread(new Runnable() {

                public void run() {
                    Y.staticSleep();
                }
            }).start();
        }

        for (int i = 0; i < 10; ++i) {
            new Thread(new Runnable() {

                public void run() {
                    new Y().instanceSleep();
                }
            }).start();
        }
    }
}

印刷:

Start instance sleep
Start instance sleep
Start instance sleep
Start instance sleep
Start instance sleep
Start static sleep
Start instance sleep
Start instance sleep
Start instance sleep
Start instance sleep
Start instance sleep
End instance sleep
End instance sleep
End instance sleep
End instance sleep
End instance sleep
End instance sleep
End instance sleep
End instance sleep
End instance sleep
End instance sleep
End static sleep
Start static sleep
End static sleep

所以这与实例static synchronized上的方法无关......synchronized

当然,如果static synchronised在整个系统中都使用方法,那么您可以期望它们对多线程系统的吞吐量产生最大影响,所以使用它们需要您自担风险……

于 2013-03-11T01:25:14.487 回答
4

你是对的——实际的锁是在Class实例本身上,而不是在类的任何实例上(更不用说所有实例了)——但我认为你对链接页面的解释过于字面了。它本身使用短语“静态锁(类锁)”,因此它的作者清楚地知道锁定是如何工作的。但是,如果您在不同的线程中有许多实例都使用同步的静态方法,那么这些实例将相互锁定。同步的静态方法不会导致同步实例方法的阻塞,但无论如何问题都存在。

于 2013-03-11T01:15:22.147 回答
2

它没有说“锁定类的所有实例”。它说'锁定类所有实例。它的措辞很糟糕,确实不正确,但它并没有说出你所说的话。

于 2013-03-11T01:50:00.877 回答
2

同步方法在执行之前获取监视器(第 17.1 节)。对于类(静态)方法,使用与方法类的 Class 对象关联的监视器。对于实例方法,使用与 this(调用该方法的对象)关联的监视器。

http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.3.6

于 2016-02-10T23:18:40.843 回答