Java同步静态方法:锁定对象或类

Posted

技术标签:

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

Java 文档说:

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

这对静态方法意味着什么?由于静态方法没有关联对象,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);
        
    

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

    当使用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
    

    当使用相同对象的线程尝试同时访问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
    

    当使用不同对象的线程尝试访问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
    

    当使用不同对象的线程尝试访问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文字的单独锁并进入静态同步方法。

【讨论】:

以上是关于Java同步静态方法:锁定对象或类的主要内容,如果未能解决你的问题,请参考以下文章

多线程

java复习总结2

Java学习笔记八---类的静态变量与静态方法的访问与调用方式

Python-面向对象高级语法之静态方法类方法

Java多线程6:synchronized锁定类方法volatile关键字及其他

java中,用synchronized会锁定当前对象,这个对象指的是它包涵的代码块,还是一个类实例