可以有多个线程在JAVA中执行静态同步方法

时间:2015-02-25 22:05:05

标签: java multithreading

我一直在网上寻找有关如何允许执行静态同步方法的信息。我发现静态同步方法将获得类锁。据我所知,这可以确保只允许所有现有类实例中的一个执行静态同步方法。这是真的吗?可以有两个类实例同时执行静态同步方法吗?因此,为了使其更具视觉效果,我添加了一个代码示例。

public class A {

   private static synchronized void m1() {
      //Print something
   }

   private synchronized void m2() {
      //Print something else
   }
}

据我所知,因为静态方法正在获取类级别监视器而非静态方法正在获取对象级别监视器,所以两者都可以同时从2个不同的线程执行,如下所示:

A a = new A;
a.m2();//object-level lock acquired
a.m1();//Class-level lock acquired

但是如果我们有上述类的3个实例,它们是否可以同时运行m1()?我认为他们不能,但我不确定。那会发生这种情况吗?

A a = new A;
A aa = new A;
A aaa = new A;

a.m1();
aa.m1();
aaa.m1();

2 个答案:

答案 0 :(得分:6)

  

据我所知,这可以确保只允许所有现有类实例中的一个执行静态同步方法。

不,这意味着一个线程将能够执行该方法,并且同步锁是特定类加载器中的Class对象。

正如the specification中所述:

  

同步方法在执行之前获取监视器(§17.1)。

     

对于类(静态)方法,使用与方法类的Class对象关联的监视器。


  

是否有两个类实例同时执行静态同步方法?

“类实例”是一个模糊的术语。如果你的意思是一个班级,那是无关紧要的;这是一个static(类范围)方法,而不是实例方法。如果您的意思是Class 对象,那么这就是我上面提到的类加载器的原因:在不常见的情况下,您有多个类加载器,您可以拥有多个Class对象 - 例如,类的副本(不是实例)。如果您有多个Class对象,则每个对象都有自己的static方法副本。因此,由于在不同对象上同步了两个副本,因此一个线程可以调用其中一个的synchronized static方法,而另一个线程正在调用synchronized static其中一个不同的成员。

但这是一个边缘案例。在正常情况下,这不是问题:您只有该方法的一个副本,并且它在Class对象的一个​​副本上同步,并且在任何给定时间只有一个线程可以执行它。

答案 1 :(得分:0)

除了另一个答案之外,还要澄清一点:

通常,没有两个线程可以访问在类监视器上同步的静态方法。

但这并不与班级本身的实例有任何关系。您不需要拥有该类的实例,以便让线程运行该静态方法。该方法是静态的,因此即使没有类的实例也可以使用它。也就是说,内存中不存在A,但您可以访问其方法。

例如,如果您的方法是公开的,则可以执行以下操作:

public class Test extends Thread {
    @Override
    public void run() {
        A.m1();
    }

    public static void main( String args[] ) {
       Test thread1 = new Test();
       Test thread2 = new Test();

       thread1.start();
       thread2.start();
    }
}

运行此Test课程,您有两个主题,他们会拨打A.m1(),并且由于它在班长监视器上已同步,因此他们不会能够在同一时间完成。但这里的要点是内存中甚至没有一个A类型的对象。他们直接使用静态方法,而不创建实例。 A方法无需运行A.m1()方法!

现在,您将方法设为私有。所以除非你有A的实例,否则你可能会认为没有办法使用它。但事实并非如此。您只能在同一个类的主体中使用它,但是,您仍然可以在A中编写一个能够创建两个线程的静态方法(例如,扩展Thread的静态嵌套类),这两个线程将运行A.m1(),而不会创建A的单个实例。

这就是为什么我们坚持该规则适用于主题而不是 A 的实例。线程数与实例数无关。


对于类对象:类对象(如A.class)属于Class类型,它在Java中用于从一个级别表示类及其功能。您可以使用它来创建类的实例,而无需使用单词new,调用其方法以及许多其他用途。

因此,A具有这样的类对象,该对象是静态方法的监视器。加载该类时会创建一个类对象。当课程需要课程时,通常会加载课程。

但在某些情况下,程序中有多个类加载器,从理论上讲,它可能会发生两个加载A.class文件(或从网上下载,或者从其他文件中获取)来源,它并不重要 - 只是它们都使用相同的字节代码,具有相同的名称和包,然后加载它。

在这种情况下,您可能有两个具有相同名称的类,但Java并未将它们视为同一个类。如果你得到一个实例(相当于new A())然后得到另一个实例,它或多或少会像两个不同的类具有相同的确切代码。在这种情况下,因为就Java而言,尽管它们具有相同的名称,但它们不是同一个类,那么您可以让一个线程运行" A#1"的方法m1。和一个运行{#1}}" A#2"的线程,他们可以同时执行此操作。