【问题标题】:My Long pending confusion on multi-threading in Java. Need help我对 Java 中的多线程的长期悬而未决的困惑。需要帮忙
【发布时间】:2010-02-14 09:22:43
【问题描述】:

我对多线程有这样的疑问,并且在许多采访中我都遇到了很多关于多线程的问题。

我说了很多关于获取对象上的锁的内容。我的疑问是当您有两个同步的方法并且有两个线程想要访问这两个方法时,ThreadA 想要访问 MethodA 而 ThreadB 想要访问 MethodB。

现在这两个方法都在同一个对象中。但是我过去常说获取对象的锁定,而我没有听说过获取方法的锁定。现在两个线程都可以并行访问 MethodA 和 MethodB 吗?我的假设是一旦你获得对象的锁定,就没有其他线程在它上面工作。不是吗?

以及synchronized(XYZ.class)的意义是什么?

【问题讨论】:

标签: java multithreading


【解决方案1】:

不,他们不能。如果我理解正确,那么您的意思是:

class Foo {

   public synchronized void methodA () {
      doSmth ();
   }

   public synchronized void methodB () {
      doSmthElse ();
   }

}

在这种情况下,同步修饰符等于:

class Foo {

   public void methodA () {
      synchronized (this) {
         doSmth ();
      }
   }

   public void methodB () {
      synchronized (this) {
         doSmthElse ();
      }
   }

}

这意味着在每个 Foo 对象的这 2 种方法之一中,只有 1 个线程可以同时工作。

以及synchronized(XYZ.class)的意义是什么?

这就是你在幕后所拥有的

class XYZ {
   public static synchronized void someMethod () { ... }
}

【讨论】:

  • 因此,强烈不建议在方法上放置同步关键字。不是吗?
  • 一般情况下,是的。这不是因为同步方法不好。如果您的小方法的所有行都不是线程安全的(即同步是必要的),那么同步方法可能是您真正需要的。但是,如果您只能将部分方法放入同步 {} 块中,则应使用此方法,并且在这种情况下应避免使用同步方法。
  • 我对这个同步(XYZ.class)还有一个疑问。您的意思是说,访问 someMethod() 的人将获得整个类的锁,并且该类的所有实例都将无法访问。是吗?
  • XYZ类的所有static synchronized方法和synchronized (XYZ.class) 将无法访问。
  • 严格来说,任何在 XYZ.class 上获得监视器的东西都将无法访问。由于 XYZ.class 是一个全局可访问的引用,谁知道它可能是什么?这就是为什么在私有引用上同步是一种更好的做法。
【解决方案2】:

将方法声明为已同步基本上与将整个方法主体包含在“synchronized(this) { ... }”块中相同。因此,同步方法上的锁定是在整个对象实例上,这意味着您将锁定其他想要在同一对象上使用同步方法的人。 如果您想要按方法同步而不是按对象同步,则必须在其他东西上进行同步,方法是在该特定方法中使用 synchronized(guardObject) 或使用 java 中的 Lock 对象之一(其中大部分是在 1.5 中添加的)。

以及synchronized(XYZ.class)的意义是什么?

这意味着您将类用作保护对象,这意味着类本身(而不是实例)中的锁将保护对块的访问。使用对象实例作为保护的不同之处在于,如果您有一万个对象,那么您一次只能访问其中一个对象,而不是一次保护多个线程对实例的访问。

【讨论】:

    【解决方案3】:

    如果methodA和methodB使用不同的数据结构,并且在其他人调用methodB时调用methodA是安全的,那么这两个方法应该使用不同的锁。

    这里是synchronized(XYZ.class)的瘦身

    synchronized (XYZ.class) 是您实现上述模式的方式——它是一个同步块:

    • 获取对象上的锁(在此类中是代表 XYZ 的 java.lang.class 的实例)
    • 做了一些工作
    • 释放锁。

    关键是要记住,Java 中的每个对象都有一个锁(技术术语是监视器),一次只能由一个线程持有。您可以为您引用的任何对象请求锁定。

    方法上的同步关键字:

    public synchronized void foo() {
        //do something
    }
    

    只是语法糖:

    public void foo() {
        synchronized(this) {
            //do something
        }
    }
    

    这种方法有一个明显的缺点:由于任何人都可以获取对象的锁,因此如果外部调用者获取并持有对象锁,同步方法将无法正常工作。

    (顺便说一句,这就是为什么锁定 XYZ.class 也是一个坏主意:它是一个全局可访问的对象,你永远不知道谁可能决定获取它的锁)

    为了避免这些缺点,通常使用这种模式来代替同步方法:

    private final Object LOCK = new Object();
    public void foo() {
        synchronized(LOCK) {
            //do something
        }
    }
    

    由于没有外部调用者可以引用 LOCK 对象,因此只有这个类可以获取它的锁,并且同步方法将始终按预期工作。

    当您有两个不同的方法需要分别锁定时,通常的做法是让每个方法锁定一个不同的私有对象:

    private final Object LOCKA = new Object();
    private final Object LOCKB = new Object();
    public void foo() {
        synchronized(LOCKA) {
            //do something
        }
    }
    
    public void bar() {
        synchronized(LOCKB) {
            //do something else
        }
    }
    

    那么有可能一个线程调用 foo() 而另一个线程正在调用 bar(),但是一次只能有一个线程可以调用 foo(),并且只有一个线程可以调用 bar( ) 一次。

    【讨论】:

      【解决方案4】:

      使用synchronized(XYZ.class) 是非常严格的。这意味着由XYZ.class 保护的任何代码都将仅由一个线程同时执行。您应该注意这一点,因为它可能会导致严重的饥饿问题。

      【讨论】:

        【解决方案5】:

        在进入定义为“同步”的关键部分时进入监视器的线程 因此,您可以将每个方法(而不是整个类)定义为同步方法,甚至可以定义为方法中的代码块。 http://java.sun.com/docs/books/tutorial/essential/concurrency/locksync.html

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-02-01
          • 2011-06-11
          • 1970-01-01
          • 2017-04-25
          • 1970-01-01
          相关资源
          最近更新 更多