同步实例和同步块

huangapple go评论59阅读模式
英文:

Synchronized instances and Synchronized blocks

问题

Assume that we have a class with two synchronized blocks:

假设我们有一个带有两个同步块的类:

class A{

public synchronized void methodA{

// Do Something
}


public synchronized void methodB{

// Do Something
}

}

Assume that we have two threads t1 and t2 sharing the same instance of class A. If t1 will invoke methodA using its instance of class A and at the same time t2 will invoke methodB using the same instance of class A that is used by t1. According to my understanding, t2 will be blocked from using the synchronized method methodB, even if t1 is using only methodA. This is because every object has one lock and this lock is acquired by t1. Is that correct? If yes, can we conclude the same if we have two synchronized blocks instead?

假设我们有两个线程“t1”和“t2”共享类“A”的相同实例。如果“t1”使用类“A”的实例调用“methodA”,与此同时“t2”使用与“t1”使用的相同类“A”实例调用“methodB”。根据我的理解,“t2”将被阻止使用同步方法“methodB”,即使“t1”只使用“methodA”。这是因为每个对象只有一个锁,而这个锁被“t1”获取。这是正确的吗?如果是的话,如果我们有两个同步块,我们是否可以得出相同的结论?
英文:

Assume that we have a class with two synchronized blocks :

class A{

public synchronized void methodA{

// Do Something
}


public synchronized void methodB{

// Do Something
}

}

Assume that we have two threads t1 and t2 sharing the same instance of class A. If t1 will invoke methodA using its instance of class A and at the same time t2 will invoke methodB using the same instance of class A that is used by t1. According to my understanding, t2 will be blocked from using the synchronized method methodB , even if t1 is using only methodA. This is because every object has one lock and this lock is acquired by t1. Is that correct ? If yes, can we conclude the same if we have two synchronized blocks instead ?

答案1

得分: 5

Yes.

这个:

synchronized void foo() {
  code();
}

只是简写形式:

void foo() {
  synchronized (this) {
    code();
  }
}

出于同样的原因,拥有一个公共字段通常是一个非常糟糕的主意,拥有一个公共锁(而 this 通常在代码包外部可访问,可以视为“公共”的)也是一个非常糟糕的主意。通常更好的方法是:

class Example {
  private final Object lock = new Object[0];

  public void foo() {
    synchronized (lock) {
      code();
    }
  }
}

如果你使锁变为公共的(例如使用 synchronized 或以其他方式锁定外部直接控制的某些代码),你必须对此进行文档化,并详细说明你在将来更新中承诺做什么和不做什么。

英文:

Yes.

This:

synchronized void foo() {
  code();
}

is simply shorthand for:

void foo() {
  synchronized (this) {
    code();
  }
}

For the same reason having a public field is generally a really bad idea, having a public lock (and this is, usually, 'public', in the sense that code from outside the package has access to it) is a really bad idea. It's usually better to do:

class Example {
  private final Object lock = new Object[0];

  public void foo() {
    synchronized (lock) {
      code();
    }
  }
}

if you make the lock public (as in, use synchronized, or otherwise lock on something code outside of your direct control has access to), you must document this, and be very detailed about what you promise to do and not do with that lock in future updates.

答案2

得分: 4

t2 将被阻止使用同步方法 methodB,即使 t1 仅使用 methodA

我们实际上并不需要使用 synchronized 来阻止两个线程调用相同的方法。这不是重点。方法是不可变的。两个线程同时进入同一个方法本身不会造成伤害。问题出现在两个(或更多)线程访问相同的数据时。

如果两个线程在两个不同的对象上调用相同的实例方法,那可能是可以的。但如果两个线程在同一个对象上调用两个不同的方法,那时我们希望使用 synchronized 来阻止任何线程看到对象处于无效状态(或更糟糕的是,防止任何线程将对象保持在无效状态)。

英文:

Not an answer to your question (rzwitzerloot did a fine job of that,) but

> t2 will be blocked from using the synchronized method methodB , even if t1 is using only methodA.

We don't really ever need synchronized to prevent two threads from calling the same method. That's not the point. Methods are immutable. No harm can come from two threads being in the same method per se. The trouble comes when two (or more) threads access the same data.

If two threads call the same instance method on two different objects, that's probably OK. If two threads call two different methods on the same single object, that's when we want synchronized to prevent any thread from seeing the object in an invalid state (or worse, to prevent any thread from leaving the object in an invalid state.

答案3

得分: 0

你的代码

```Java
public final class A
{
    public synchronized void methodA
    {
        // Do Something
    }
    
    public synchronized void methodB
    {
        // Do Something
    }
}

等同于

public final class A
{
    public void methodA
    {
        synchronized( this )
        {
            // Do Something
        }
    }
    
    public void methodB
    {
        synchronized( this )
        {
            // Do Something
        }
    }
}

所以基本上,你的假设是正确的,你可以使用两个 synchronized 块来代替 synchronized 方法,以达到相同的效果。

重要的是,两个方法中的 synchronized 语句都使用相同的对象作为参数!在这种情况下,它是实例本身,因此很容易实现。

但我也在一些代码中看到过这种情况:

public final class A
{
    /* 不要这样做 */
    private static final Object m_LockForA = new Object [0];
    private static final Object m_LockForB = new Object [0];

    public void methodA
    {
        synchronized( m_LockForA )
        {
            // Do Something
        }
    }
    
    public void methodB
    {
        synchronized( m_LockForB )
        {
            // Do Something
        }
    }
}

这种方式也能工作(也许甚至是故意以这种方式工作的...),但与你原始的代码完全不同。


<details>
<summary>英文:</summary>

Your code

```Java
public final class A
{
    public synchronized void methodA
    {
        // Do Something
    }
    
    public synchronized void methodB
    {
        // Do Something
    }
}

is equivalent to

public final class A
{
    public void methodA
    {
        synchronized( this )
        {
            // Do Something
        }
    }
    
    public void methodB
    {
        synchronized( this )
        {
            // Do Something
        }
    }
}

So basically, you assumption is correct, you can have two synchronized blocks instead of synchronized method to achieve the same effect.

The important thing is that the synchronize statements in both methods use the same object as 'argument'! In this case it is the instance itself, therefore it is easy.

But I have also seen this in some code:

public final class A
{
    /* DO NOT DO THIS */
    private static final Object m_LockForA = new Object [0];
    private static final Object m_LockForB = new Object [0];

    public void methodA
    {
        synchronized( m_LockForA )
        {
            // Do Something
        }
    }
    
    public void methodB
    {
        synchronized( m_LockForB )
        {
            // Do Something
        }
    }
}

This will work somehow (and perhaps it is even intended to work exactly in that way …) but completely different from your original code.

huangapple
  • 本文由 发表于 2023年4月11日 04:59:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/75980680.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定