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


148

Java文档说:

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

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

Answers:


129

由于静态方法没有关联的对象, 所以synced关键字会锁定在类而不是对象上吗?

是。:)


81
请回答详尽,以便所有人都能理解。
Madhu

6
@马杜 这意味着,如果您在同一类上有2个或更多同步方法,则即使该类有多个实例,两个方法也不能同时执行。对于每个同步方法,该锁定本质上与对Object.class进行锁定相同。
史蒂文

这个答案是错误的- this是在实例方法上获得的锁-请修复此问题。
vemv 2012年

1
@vemv问题是关于类方法,而不是实例方法。
OscarRyz 2012年

23
@vemv是的,要理解答案,您必须先阅读问题。
OscarRyz 2012年

199

只是为Oscar的回答(很简洁!)添加了一些细节,Java语言规范中的相关部分是8.4.3.6,“同步方法”

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


17
有用,我一直在寻找该报价+1
OscarRyz

80

您必须要注意的一点(几个程序员通常会陷入该陷阱)是同步的静态方法与同步的非静态方法之间没有链接,即:

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

主要:

A a = new A();

线程1:

A.f();

线程2:

a.g();

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


18
但是如果g()突变了f()正在读取的某个静态变量,该怎么办?我们如何使该线程安全?那我们是否明确获得了该类的锁?
巴斯金2011年

22
是的,你非静态方法必须明确的类本身(即同步synchronized (MyClass.class) {...}
jfpoilpret

@jfpoilpret“ synchronized(MyClass.class){...}”等同于使此方法静态同步,对吗?
crazymind

15

除非您按以下方式实现g():

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

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


63
请注意,这里实际上可能会存在一些非常微妙而令人讨厌的错误。记住getClass()返回运行时类型;如果您将该类作为子类,则父类和子类将在不同的锁上同步。synchronized(MyClass.class)如果需要确保所有实例都使用相同的锁,这是一种方法。
考恩

4

看看有关本征锁和同步的 oracle文档页面

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


2

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

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

Customer c = new Customer();

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

因此静态方法或属性也有一个对象


2

下面的示例使类锁和对象锁之间的关系更加清晰,希望下面的示例也能对其他人有所帮助:)

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

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

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.