【问题标题】:Java synchronized static methods: lock on object or classJava同步静态方法:锁定对象或类
【发布时间】:2010-10-01 01:24:30
【问题描述】:

Java 文档说:

不可能两次调用同步方法 交错的同一个对象。

这对静态方法意味着什么?由于静态方法没有关联对象,synchronized关键字会锁定类而不是对象吗?

【问题讨论】:

    标签: java class static methods synchronized


    【解决方案1】:

    为了给 Oscar 的回答添加一点细节(非常简洁!),Java 语言规范的相关部分是 8.4.3.6, 'synchronized Methods'

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

    【讨论】:

    • 有用,我在找那个报价+1
    【解决方案2】:

    由于静态方法没有关联对象,同步关键字会锁定类,而不是对象吗?

    是的。 :)

    【讨论】:

    • 请详细回答,以便大家理解。
    • @Madhu。这意味着如果您在同一个类上有 2 个或更多同步方法,即使该类有多个实例,它们也不能同时执行。锁定本质上与对每个同步方法的 Object.class 锁定相同。
    • 这个答案是错误的-this是在实例方法上获得的锁-,请奥斯卡修复它。
    • @vemv 问题是关于类方法,而不是实例方法。
    • @vemv 是的,要理解答案,您必须先阅读问题。
    【解决方案3】:

    你必须注意的一点(一些程序员通常会陷入这个陷阱)是同步的静态方法和同步的非静态方法之间没有联系,即:

    class A {
        static synchronized f() {...}
        synchronized g() {...}
    }
    

    主要:

    A a = new A();
    

    线程 1:

    A.f();
    

    线程 2:

    a.g();
    

    f() 和 g() 彼此不同步,因此可以完全同时执行。

    【讨论】:

    • 但是如果 g() 正在改变 f() 正在读取的某个静态变量怎么办。我们如何使该线程安全?那么我们是否显式地获取了类的锁?
    • 是的,您的非静态方法必须在类本身上显式同步(即synchronized (MyClass.class) {...}
    • @jfpoilpret "synchronized(MyClass.class) {...}" 相当于让这个方法静态同步吧?
    【解决方案4】:

    除非你按如下方式实现 g():

    g() {
        synchronized(getClass()) {
            ...
        }
    }
    

    当我想在对象的不同实例之间实现互斥(例如,在访问外部资源时需要)时,我发现这种模式也很有用。

    【讨论】:

    • 请注意,这里实际上可能存在一些非常微妙和讨厌的错误。记住getClass() 返回 runtime 类型;如果您将类子类化,则父类和子类将在不同的锁上同步。如果您需要确保所有实例使用相同的锁,synchronized(MyClass.class) 是您的最佳选择。
    【解决方案5】:

    查看Intrinsic Locks and Synchronization上的 oracle 文档页面

    您可能想知道调用静态同步方法时会发生什么,因为静态方法与类相关联,而不是与对象相关联。 在这种情况下,线程获取与类关联的 Class 对象的内在锁因此,对类的静态字段的访问由不同于任何类实例的锁控制

    【讨论】:

      【解决方案6】:

      静态方法也有一个关联对象。它属于 JDK 工具包中的 Class.class 文件。当 .class 文件加载到 ram 中时,Class.class 会创建它的一个实例,称为模板对象。

      例如:- 当您尝试从现有客户类创建对象时,例如

      Customer c = new Customer();
      

      将 Customer.class 加载到 RAM 中。在那一刻,JDK 工具包中的 Class.class 创建了一个名为 Template 对象的对象,并将该 Customer.class 加载到该模板对象中。该 Customer.class 的静态成员成为该模板对象中的属性和方法。

      所以一个静态方法或属性也有一个对象

      【讨论】:

        【解决方案7】:

        下面的例子更清楚地说明了类和对象锁,希望下面的例子对其他人也有帮助:)

        例如,我们有以下方法,一个是获取类,另一个是获取对象锁:

        public class MultiThread {
        
            public static synchronized void staticLock() throws InterruptedException {
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(100);
                    System.out.println(Thread.currentThread().getName() + " " + i);
                }
            }
        
            public synchronized void objLock() throws InterruptedException {
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(100);
                    System.out.println(Thread.currentThread().getName() + " " + i);
                }
            }
        }
        

        所以,现在我们可以有以下场景:

        1. 当使用same Object的线程尝试同时访问objLock OR staticLock方法时(即两个线程都尝试访问相同的方法)

          Thread-0 0
          Thread-0 1
          Thread-0 2
          Thread-0 3
          Thread-0 4
          Thread-1 0
          Thread-1 1
          Thread-1 2
          Thread-1 3
          Thread-1 4
          
        2. 当使用相同对象的线程尝试同时访问staticLockobjLock方法时(尝试访问不同的方法)

          Thread-0 0
          Thread-1 0
          Thread-0 1
          Thread-1 1
          Thread-0 2
          Thread-1 2
          Thread-1 3
          Thread-0 3
          Thread-0 4
          Thread-1 4
          
        3. 当使用不同对象的线程尝试访问staticLock方法时

          Thread-0 0
          Thread-0 1
          Thread-0 2
          Thread-0 3
          Thread-0 4
          Thread-1 0
          Thread-1 1
          Thread-1 2
          Thread-1 3
          Thread-1 4
          
        4. 当使用不同对象的线程尝试访问objLock方法时

          Thread-0 0
          Thread-1 0
          Thread-0 1
          Thread-1 1
          Thread-0 2
          Thread-1 2
          Thread-1 3
          Thread-0 3
          Thread-0 4
          Thread-1 4
          

        【讨论】:

          【解决方案8】:

          对于那些不熟悉锁定在类对象上的静态同步方法的人,例如对于字符串类,它的 String.class 实例同步方法锁定在 Java 中由“this”关键字表示的 Object 的当前实例。由于这两个对象不同,它们具有不同的锁,因此当一个线程正在执行静态同步方法时,java中的其他线程不需要等待该线程返回,而是将获取表示为字节.class文字的单独锁并进入静态同步方法。

          【讨论】:

            猜你喜欢
            • 2013-08-06
            • 1970-01-01
            • 1970-01-01
            • 2011-03-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-09-03
            相关资源
            最近更新 更多