【问题标题】:Concurrency in Java: synchronized static methodsJava 中的并发:同步的静态方法
【发布时间】:2023-03-25 07:39:01
【问题描述】:

我想了解如何对 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(),但是这些方法获取不同的锁。我的问题是这是如何预防的,在上面的代码中是否可以预防。

【问题讨论】:

    标签: java multithreading synchronized


    【解决方案1】:

    Static 锁附加到class 定义,因此在该的所有实例之间共享。

    none static 方法的同步只适用于类的当前实例(锁在类instance 上,例如this)。在您的示例中,您有两个没有相互关系的不同锁。

    我不希望两个线程同时调用 f.get() 和 Foo.inc(),但是这些方法获取不同的锁。我的问题是这是如何预防的,在上面的代码中是否可以预防

    必须共享一个锁才能仲裁对f.getFoo.inc() 的访问。您可以通过共享同一个静态锁或同一个实例锁来做到这一点。

    【讨论】:

      【解决方案2】:

      非静态同步调用也不获取类本身的锁。 (并且静态同步块不会锁定从该类实例化的任何对象。)

      换句话说,调用f.get()(锁定f)和Foo.inc()(锁定类Foo)可以同时运行。它们不是“同步的”。

      您可以使用不同的模式(单例),或将所有方法设为静态。

      【讨论】:

      • 不确定我是否理解您的评论 - 静态同步方法确实获得了 Class 上的锁定,但没有获得使用它创建的任何对象的锁定(Foo a = new Foo(); - Foo.inc() 锁定返回的东西a.getClass(),但不是a)。还是我错了?
      • Foo.inc()new Foo().inc() 将相互锁定,如果声明为 static,它们也会锁定 Foo.dec()
      • 是的,因为他们都锁定了班级。并且这些都不会锁定从该类实例化的对象,例如我之前评论中的 a 或您未命名的新 Foo 对象。我看不出答案中的措辞有什么问题。
      【解决方案3】:

      同步的静态方法实际上等效于:

      public static void foo() {
          synchronized (ClassName.class) {
              // Body
          }
      }
      

      换句话说,它锁定与声明方法的类关联的Class 对象。

      来自section 8.4.3.6 of the JLS

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

      【讨论】:

        【解决方案4】:

        这两个调用彼此不同步。 正如你所说,f.get() 的调用者获得了f 对象的锁,Foo.inc() 的调用者获得了Foo.class 对象的锁。因此,同步规则与使用另一个对象调用实例同步方法而不是静态调用相同。

        【讨论】:

          【解决方案5】:

          静态和实例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(); }
          }
          

          【讨论】:

            【解决方案6】:

            如果你读到http://download.oracle.com/javase/tutorial/essential/concurrency/locksync.html

            它会告诉你:

            你可能想知道当一个 调用静态同步方法, 因为关联了一个静态方法 有一个类,而不是一个对象。在这个 情况下,线程获取 Class 对象的内在锁 与类相关联。从而访问 对类的静态字段进行控制 通过一个不同于 锁定该类的任何实例。

            它告诉你所有你需要知道的。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多