Java中同步方法和同步块有什么区别?

38

Java中的同步方法和同步块有什么区别?

我在网上搜索了答案,人们似乎对这个问题不太确定 :-(

我的看法是两者之间没有区别, 只是同步块的范围更局部,因此锁定时间更短 ??

在静态方法中锁定时,锁定对象是什么?什么是类锁?


@try-catch-finally 这不是这个问题的重复;那个问题问的是同步方法是否仅提供对该方法的独占访问,还是对整个对象的访问。 - Mark Rotteveel
@MarkRotteveel,就不同的标题而言,您是正确的。另一方面,其他问题的答案也涵盖了差异:将同步部分限定在某些语句中(或不限定),以及控制锁定位置(或无法控制)。 - try-catch-finally
6个回答

46

同步方法使用方法接收者作为锁(即非静态方法使用this,而静态方法使用封闭类)。synchronized块使用表达式作为锁。

因此,下面两个方法在锁定方面是等效的:

synchronized void mymethod() { ... }

void mymethod() {
  synchronized (this) { ... }
}
对于静态方法,类将被锁定:
class MyClass {
  synchronized static mystatic() { ... }

  static mystaticeq() {
    syncrhonized (MyClass.class) { ... }
  }
}

同步块中,您可以使用任何非null对象作为锁:

synchronized (mymap) {
  mymap.put(..., ...);
}

锁范围

对于同步方法,锁将在整个方法范围内保持,而在 synchronized 块中,锁仅在该块的范围内保持(也称为临界区)。实际上,如果JVM可以证明可以安全地执行操作,则允许通过优化从 synchronized 块执行中删除一些操作。


我在一段时间前阅读过(但不记得来源了)使用方法级别的同步,即使该同步块封装了整个方法体,JVM 内部也可以更容易地进行优化。这可能是一个有趣的微基准测试。 - Gregory Mostizky
1
在锁定范围内,您可能意味着“...而在同步块中,锁仅在同步块期间保持”,而不是“...而在同步方法中,锁仅在同步块期间保持”。 - tonymontana
2
根据这篇文章,与同步块相比,JVM为同步方法创建的字节码较少。@Gregory Mostizky http://www.ibm.com/developerworks/java/library/j-5things15/index.html?ca=drs- - Kin

9

同步方法是一种简写方式。这样写:

class Something {
    public synchronized void doSomething() {
        ...
    }

    public static synchronized void doSomethingStatic() {
        ...
    }
}

就所有意图而言,这与以下内容等同:

class Something {
    public void doSomething() {
        synchronized(this) {
            ...
        }
    }

    public static void doSomethingStatic() {
        synchronized(Something.class) {
            ...
        }
    }
}

(其中Something.class是类Something的类对象。)

因此,使用同步块时,您可以更具体地锁定并更精细地控制何时使用它,但除此之外没有任何区别。


5

是的,这是其中一个区别。另一个区别是你可以在除了this之外的其他对象上获取锁。


2
关键的区别在于:如果你声明一个方法为synchronized,那么整个方法体都会变成synchronized;然而,如果你使用synchronized块,则可以只将方法的“关键部分”包围在synchronized块中,而将方法的其余部分留在块外。
如果整个方法都是关键部分,那么实际上没有区别。如果不是这种情况,那么你应该在关键部分周围使用synchronized块。你在synchronized块中有越多语句,你得到的并行性就越少,所以你应该尽量减少语句数量。

快速跟进:这是否意味着同步块比同步方法使用起来更加经济? - Everyone
如果同步块包含函数的所有内容,则没有区别。但是,如果您使用同步块,则可以仅围绕关键部分进行同步(可能会将某些计算留在同步区域之外)。如果您这样做,您的程序将运行得更快。 - Michael Aaron Safyan

0

同步方法会锁定包含该方法的对象实例。

而同步块可以锁定任何对象 - 通常是作为实例变量定义的互斥对象。这允许更多对正在运行的锁进行控制。


0
我的看法是两者之间没有区别,除了同步块可能更局部化,因此锁定时间会更短吗?
是的,你说得对。与同步方法不同,同步语句必须指定提供内在锁的对象。
来自Java教程的示例:
public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

同步语句对于通过细粒度同步来提高并发性也非常有用。您可以在同一教程页面上找到以下用例的良好示例。

例如,假设类MsLunch有两个实例字段和,它们从未同时使用。这些字段的所有更新都必须进行同步,但没有理由防止对的更新与对的更新交错 - 这样做会通过创建不必要的阻塞来降低并发性。相反,我们创建了两个对象,仅用于提供锁,而不是使用同步方法或以其他方式使用与此关联的锁。

在静态方法上锁定时,锁定的是什么?类上的锁有什么意义?

在这种情况下,线程获取与类相关联的Class对象的内部锁定。因此,对类的静态字段的访问受到与类的任何实例的锁定不同的锁定控制。

当您将方法设置为synchronized(非static)时:

synchronized 方法在同一对象上的两次调用之间不可能交错执行。 当一个线程正在执行对象的 synchronized 方法时,所有尝试调用该对象上的 synchronized 方法的其他线程都会被阻塞,直到第一个线程完成为止。

如果您将方法声明为 static synchronized

不可能让同一类不同对象的两个static synchronized 方法调用交错执行。 当一个线程正在执行 Class A 对象的 static synchronized 方法时,所有尝试调用 Class A 的任何对象上的 static synchronized 方法的其他线程都会被阻塞,直到第一个线程完成此方法的执行。

在这个SE问题中,您可以找到更好的替代同步的方法:

Java中如何避免使用synchronized(this)?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接