我想了解如何在Java中对静态方法进行锁定。
假设我有以下课程:
class Foo {
private static int bar = 0;
public static synchronized void inc() { bar++; }
public synchronized int get() { return bar; }
我理解当我调用f.get()
时,线程获取对象f
的锁定,当我执行Foo.inc()
时,线程获取类Foo
上的锁定}。
我的问题是两个呼叫如何相互同步? 调用静态方法是否也获取了对所有实例化的锁定,或者相反(这似乎更合理)?
修改
我的问题并不完全是static synchronized
的工作原理,但静态和非静态方法是如何相互同步的。
即,我不希望两个线程同时调用f.get()
和Foo.inc()
,但这些方法获取不同的锁。我的问题是这是如何可以预防的,并且在上面的代码中被阻止了。
答案 0 :(得分:9)
静态和实例synchronized
方法彼此无关,因此您需要在它们之间应用一些额外的同步,如下所示:
class Foo {
private static int bar = 0;
public static synchronized void inc() { bar++; }
public synchronized int get() {
synchronized (Foo.class) { // Synchronizes with static synchronized methods
return bar;
}
}
}
(虽然在这种情况下,在synchronized
上留下get()
没有意义,因为它不会做任何需要在实例上进行同步的事情。)
小心死锁 - 因为此代码需要多个锁,所以它应该按照一致的顺序执行,即其他同步的静态方法不应该尝试获取实例锁。
另请注意,使用原子字段可以在没有同步的情况下解决此特定任务:
class Foo {
private static AtomicInteger bar = new AtomicInteger(0);
public static void inc() { bar.getAndIncrement(); }
public int get() { return bar.get(); }
}
答案 1 :(得分:8)
同步静态方法实际上等同于:
public static void foo() {
synchronized (ClassName.class) {
// Body
}
}
换句话说,它锁定了与声明该方法的类关联的Class
对象。
synchronized方法在执行之前获取监视器(第17.1节)。对于类(静态)方法,使用与方法类的Class对象关联的监视器。对于实例方法,使用与此关联的监视器(调用该方法的对象)。
答案 2 :(得分:3)
如果您阅读http://download.oracle.com/javase/tutorial/essential/concurrency/locksync.html。
会告诉你:
你可能想知道什么时候会发生什么 调用静态同步方法, 因为静态方法是相关的 与类,而不是对象。在这 情况下,线程获取 Class对象的内部锁 与班级相关联。因此访问 对类的静态字段进行控制 通过一个与众不同的锁 锁定任何类的实例。
告诉你所有你需要知道的事情。
答案 3 :(得分:2)
同样,非静态同步调用不会获取类本身的锁定。 (并且静态同步块不会锁定从该类实例化的任何对象。)
换句话说,调用f.get()
(锁定f
)和Foo.inc()
(锁定类Foo
)可以同时运行。它们不是“同步的”。
您可以使用不同的模式(单例),或使所有方法都是静态的。
答案 4 :(得分:1)
Static
锁定附加到class
定义,因此在类的所有实例之间共享。
none static
方法的同步仅适用于类的当前实例(锁定在类实例上,例如this
)。在您的示例中,您有两个不同的锁,没有相互关系。
我不希望两个线程同时调用f.get()和Foo.inc(),但这些方法获取不同的锁。我的问题是这是如何可以预防的,并且在上面的代码中被阻止了
您必须共享锁定才能仲裁对f.get
和Foo.inc()
的访问权限。您可以通过共享相同的静态锁或通过相同的实例锁来执行此操作。
答案 5 :(得分:0)
这两个调用不会相互同步。
正如您所说,f.get()
的调用者获取f
对象的锁定,Foo.inc()
的调用者获取Foo.class
对象的一个。因此,同步规则与使用另一个对象调用实例同步方法而不是静态调用相同。