关于Java中的synchronized关键字的问题(以及C#的锁定)

时间:2011-01-04 08:54:19

标签: c# java multithreading synchronized

  1. Java中的以下类是否相同?如果不是,为什么?

    class Abc {
        private int c = 0;
    
    
    
    public synchronized void add(int a)  {
        c += a;
    }
    
    
    public synchronized void subtract(int a) {
        c -= a;
    
    } } class Def { private int c = 0; private final Object lock = new Object();
    public void add(int a)  {
        synchronized(lock) {
             c += a;
        }
    }
    
    
    public void subtract(int a) {
        synchronized(lock) {
            c -= a;
        }
    
    } }
  2. 此外,在public synchronized void add(int a) { c += a; } public synchronized void subtract(int a) { c -= a; 中使用public void add(int a) { synchronized(lock) { c += a; } } public void subtract(int a) { synchronized(lock) { c -= a; } 作为同步参数而不是Def会出现什么问题?以下是问题吗?

    this
  3. Java中的synchronized语句就像C#的锁定语句一样吗?如果不是,他们的区别是什么?如果是,为什么C#也不允许锁定方法,如Java允许?

  4. 所以我想以下示例可以描述lock的问题?

    Def def = new Def()
    synchronized (def) {
        def.add(5); //originates deadlock? or is this allowed AS LONG
                    //as all this happens in the same thread?
    }
    

    Thread1调用synchronized (this),同时Thread2尝试调用class Xyz { private int c;

            public void add(int a)  {
                synchronized(this) {
                     c += a;
                }
            }
    
    
            public void subtract(int a) {
                synchronized(this) {
                    c -= a;
                }
           }
    
    
           public void show666() {
               return 666;
           }
    
    } 。  Thread2必须等待Thread1以 public void add(int a) { synchronized(this) { c += a; } } public void subtract(int a) { synchronized(this) { c -= a; } } public void show666() { return 666; } 结束  它不需要任何与锁直接相关的信息。是吗?

5 个答案:

答案 0 :(得分:3)

  1. 不,他们是不同的。使用synchronized(reference){}获取给定引用(锁定对象)上的监视器锁定,并在方法声明中使用synchronized使用this作为监视器锁定。最终结果是,外部调用者无法获得与addsubtract方法相同的锁定。

  2. 不存在死锁,您可以保存任意数量的监视器锁,而Def对象使用不同的对象来锁定。但即使是第一堂课Abc,它也不会陷入僵局。 java中的同步锁是重要的。因此,您可以在理论上尝试将它们锁定在同一个线程中,并且可以根据需要多次锁定它们。

  3. 令人惊讶的是,C#不允许在方法上使用lock,但在引用时使用它时,似乎与java synchronized关键字相似。 C#的设计目标之一是足够熟悉Java开发人员能够在没有完全替换思想的情况下接收它,所以我想这并不太令人惊讶。

答案 1 :(得分:3)

  

1 Java中的以下类是否相同?如果不是,为什么?

它们因同步的实例而不同,因此会受到这些实例之间差异的影响。

Abc使用调用该方法的Abc实例。其他可以访问实例的代码也可以通过显式使用同步块中的实例来使用它进行同步。

Def中的同步代码块显式命名与它们同步的实例。由于此实例对Def实例是私有的,因此Def外部的代码无法使用它进行同步(除非实例以某种方式泄露)。

有些人可能认为Def方法更安全,因为你应该使用私有实例变量的原因,封装你的锁是很重要的。

最后,synchronized关键字和使用synchronized语句之间存在差异,例如: synchronized语句可以锁定任何对象,而不仅仅是执行代码的实例,synchronize关键字稍微高效等等。

  

2a另外,在Def中会出现什么问题,使用它作为synchronized参数而不是lock?

没问题。在实例方法上使用synchronized关键字在语义上与在this上同步的同步块中包装方法的代码相同(synchronized关键字稍微更高效)。在静态方法上使用synchronized关键字与使用在类本身上同步的同步块(例如synchronized(FooBar.class) { ... })相同。

  

2b以下问题是什么?

不,Java中的锁是可重入的,这只意味着保护实例上持有锁的线程可以进入和退出同一实例上同步的任何其他代码块。

  

3a Java中的synchronized语句就像C#的锁语句一样吗?如果不是,他们的区别是什么?

在语义上等效。

http://en.csharp-online.net/CSharp_FAQ:_What_is_the_difference_between_CSharp_lock_and_Java_synchronized#Synchronized_code_blocks

但请注意关于Monitor.EnterMonitor.Exit

的回答

Are there any differences between Java's "synchronized" and C#'s "lock"?

  

3b如果是,为什么C#也不允许锁定方法,如Java允许?

确实如此 - 使用[MethodImpl(MethodImplOptions.Synchronized)]注释。

http://en.csharp-online.net/CSharp_FAQ:_What_is_the_difference_between_CSharp_lock_and_Java_synchronized#Synchronized_methods

  

4 Thread1调用xyz.add(0),同时Thread2尝试调用xyz.show666()。 Thread2必须等待Thread1完成xyz.add(0),尽管它不需要任何与锁直接相关的信息。是吗?

不,很多人认为“锁定”实例会影响整个实例。它仅影响在该实例上同步的同步代码(同步语句中的同步方法和代码)。非同步代码不受影响(至少在它遇到同步语句或调用同步方法之前)。

未同步的方法show666()不会导致线程阻塞。如果将synchronized(this)语句更改为synchronized方法,则不会发生任何更改 - 同样show666将不会阻止(除非它同步)。

答案 2 :(得分:1)

  1. 它们几乎是等价的,只有一点区别:因为Def使用内部私有对象作为锁,没有其他人可以从外部世界锁定该对象。在Abc对象的情况下,其他人可能锁定该对象然后从不同的线程调用其某些方法,这可能导致死锁。实际的可能性是微弱的(因为它显然需要代表另一个程序员的一些恶作剧或无知),但不是零。因此,有些人更喜欢Def风格是安全的,尽管AFAIK常见的习惯用法与Abc一样。

  2. Java锁是可重入的,因此从同一个线程多次调用锁是可以的。

  3. 抱歉,我不能胜任C#。

  4. 在代码示例中,您的意思是synchronized(this)而不是synchronized(lock)吗?无论如何,由于show666未同步,因此对它的调用不会阻止,即使对同步方法的另一次调用阻止在同一对象上。

答案 3 :(得分:1)

请参阅http://download.oracle.com/javase/tutorial/essential/concurrency/locksync.html

它应该可以帮助你回答1& 2,特别是它声明:

  

可重入同步

     

回想一下,线程无法获取   锁由另一个线程拥有。但是一个   线程可以获得它的锁定   已经拥有。允许一个线程   不止一次获得同一个锁   启用可重入同步。   这描述了一种情况   同步代码,直接或   间接地,也调用一种方法   包含同步代码,两者都包含   代码集使用相同的锁。   没有可重入同步,   同步代码必须采取   许多额外的预防措施要避免   有一个线程导致自己阻止。

  1. 据我所知,除非锁定是由其他人同步的,否则这两个类在外面几乎相同。

  2. 由于可重入同步

  3. ,不应导致任何特殊问题

    (3。不熟悉C#)

答案 4 :(得分:1)

1.通常,它们是不同的,因为不同对象的监视器用于同步模块。顺便说一句,如果你不使用abc类实例的监视器(特别是如果你不在abc类之外使用synchronized(abcObject){},那么这两个类将表现相同,其中abcObject是Abc类的实例)

2.A。以下两种方法是相同的:

public void add(int a)  {
    synchronized(this) {
         c += a;
    }
}

public synchronized void add(int a)  {
         c += a;
}

2.b中。我没有在该代码中看到任何问题:两个监视器(def实例的监视器和def.lock实例的监视器)总是按严格顺序锁定(没有循环等待条件)

3.无法谈论C#