5

可能重复:
同步块与同步方法?

如果有人可以帮助我提供有关方法同步与对象同步之间有什么不同的真实示例,那就太好了。

方法同步示例

public class MyClassExample {
private int i;
public synchronized void increment(){
    i = i + 1;
}
}

对象同步示例

public class MyClassExample {
private int i;
Object writeLock = new Object();
 public void increment(){
    synchronized(writeLock) {
        i = i + 1;
    }
}
}
4

3 回答 3

11

tl;dr - 外部同步使您容易受到攻击(有意或无意),并且还迫使您进行可能不必要的锁定检查。在这个答案的最底部还有书呆子的好信息。

同步方法几乎与同步方法相同(见底部):

synchroinzed void foo() {

}

void foo() {
    synchronized(this) {

    }
}

通过使方法本身不是synchronized,您可以让自己锁定任何东西Object,而不仅仅是this。我个人建议在 internal 上同步Object,就像这样

private final Object foolock = new Object();

void foo() {
    synchronzied(foolock) {

    }
}

原因是,如果您使用一种synchronized方法,可以有效地将this其他人synchronize锁定在您身上,并将您锁定在您的Object! 想象一下:

class FooDoer {
    // removed! using synchronized methods instead
    //final Object foolock = new Object();

    synchronized void foo() {

    }
}

// thread 1 - attacker
FooDoer f = new FooDoer();
globalMap.put("TheFoo",f);
synchronized(f) {
    while(true); // haha!
}

// thread 2 - victim
FooDoer f = globalMap.get("TheFoo");
f.foo(); // locked, because Thread 1 has locked us out!

它使您面临拒绝服务攻击。这不好!通过将锁定设置为内部,您作为类的作者可以准确控制谁可以锁定对象的哪些区域以及在什么条件下锁定。

另一个问题是您可能没有要检查的受保护数据。例如:

synchronized void foo() {
    if(expensiveAccessCheck()) {
        update();
    }
}

void foo() {
    if(expensiveAccessCheck()) {
        synchronized(foolock) {
            update();
        }
    }
}

在这种情况下,您不必让其他人在您坐在那里旋转车轮时等待。也许您正在从 URL 中抓取数据,然后进行更新。为什么要让其他人远离数据?在这种情况下,粒度越低越好。

现在你可能还记得我之前说的几乎相同。两者之间有一个微小的,微小的,微小的差异。同步方法会将同步指令直接烘焙到字节码中的方法签名中。这将使字节码缩短 1 个字节,因为它不需要进行额外的调用。这可能会产生很小的影响,因为方法字节码中的字节数是确定是否内联的因素之一。尽管有这些细节,我强烈建议将它们视为相同,因为这是一种微优化,在生产环境中几乎不会发挥重要作用。当它们不是相同时,将其称为相同是不完整的。

于 2012-09-12T23:46:10.843 回答
3

实例方法在synchronized上同步this

所以

public synchronized void increment(){
    i = i + 1;
}

相当于

public void increment(){
    synchronized (this) {
        i = i + 1;
    }
}
于 2012-09-12T23:35:48.647 回答
2

Java 语言规范写道

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

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

对于实例方法,使用与this(调用该方法的对象)关联的监视器。

这些是同步语句(第 14.19 节)可以使用的相同监视器。

因此,代码:

class Test {
    int count;
    synchronized void bump() {
        count++;
    }
    static int classCount;
    static synchronized void classBump() {
        classCount++;
    }
}

与以下效果完全相同:

class BumpTest {
    int count;
    void bump() {
        synchronized (this) { count++; }
    }
    static int classCount;
    static void classBump() {
        try {
            synchronized (Class.forName("BumpTest")) {
                classCount++;
            }
        } catch (ClassNotFoundException e) {}
    }
}
于 2012-09-12T23:37:44.457 回答