Java在对象类或调用类中“同步”

时间:2012-03-21 02:31:06

标签: java multithreading

我有一个多线程程序,我想知道使用“synchronized”的方式是正确的。

方式1: 我有一个类对象(将被传递给多个线程),在其中称为MyClass:

public synchronized void set(String name) {
    this.name = name;
}

方式2: 我有相同的类,但在set方法中没有“synchronized”:

public void set(String name) {
    this.name = name;
}

调用者会这样做:

MyClass myclass = new MyClass();
synchronized(myclass) {
    myclass.set("myclass");
}

谁能告诉我哪一个是实现多线程对象共享的正确方法?我很困惑这两个,我试过两个,他们似乎工作正常。

6 个答案:

答案 0 :(得分:6)

同步方法:

  • 你在一个地方做到了,
  • 来电者不必担心同步,
  • 您不需要在任何需要调用的地方复制代码
  • 最重要的是 - 如果调用者省略同步,则无法正常工作

这取决于背景,真的 - 例如在某些情况下,调用者最好进行同步,因为他们可以决定同步开销是否值得。例如,已知在一个线程上工作的所有调用者都不需要同步,这只会减慢速度。

对于所有非时间关键的事情,最好避免在凌晨3点喝第5杯咖啡时需要调试的问题,同时你的同事会挥动棒球棒并喊出一些不被提及的话给孩子们......

答案 1 :(得分:1)

第一个选项通常是正确的版本。

如果有两个单独的类从不同的线程调用MyClass.set(),则无法保证在调用set()之前它们将锁定实例。从技术上讲,根据您的示例提供的内容与您在调用set()的实例上进行同步是一回事。但是,第二个并不保证客户会一直这样做,如果他们不这样做则不会保证两个不安全。

现在技术上在你提供的示例中,两个线程无法遇到麻烦,因为如果一个线程创建了实例并调用了set()。线程没有其他方式可以访问该实例。所以永远不会有任何锁争用。这样您就知道对象创建如何影响多线程程序。请记住,两个线程必须共享对他们打算修改的实例的公共引用,以防任何线程问题成为问题。如果他们从不分享共同参考,则没有安全问题。

答案 2 :(得分:0)

通常,您将synchronized设置为与要保护的状态相同的位置。因此,如果MyClass存储了您要同步访问的名称,则应同步MyClass。

答案 3 :(得分:0)

synchronized方法保证使用对象的基本锁定正确同步所有使用该方法的尝试。这意味着调用者不会意外忘记锁定对象 - 它隐含在函数调用中。

锁定方法。请记住,您还需要同步get方法,否则它可能反映了对象的不一致状态,因为如果它不是synchronized,它将不会检查锁定并且可以在对象的锁定时并行完成在另一个主题上举行。

答案 4 :(得分:0)

如果你去同步块,它将锁定一个特定的对象。如果你去同步方法,它将锁定所有对象。请参考以下链接,可能会得到您的答案..

Is there an advantage to use a Synchronized Method instead of a Synchronized Block?

答案 5 :(得分:0)

我打算投票给第二个选项。一般来说,类不应该是线程安全的(因此不能在内部同步)。安全需要时间并且通常不需要。此外,您经常遇到以下情况:if (list.isEmpty()) list.add( filler );。如果两种方法在内部同步,则没有任何好处。第一次通话时该列表可以为空,并且在add通话时有1,000,000个条目。整个语句必须同步才有用。

更一般地说,您需要为每个类决定它是否是线程安全的。那些不是更快的并且可以由调用者设置为线程安全的,如上例所示,他们可能需要一些灵活性来实现它。此外,还有一些聪明的方法可以避免任何同步需求,例如从单个线程引用类。大多数Java Collections Framework类都不是也不应该是线程安全的。线程安全通常应该在高级而不是低级别处理。

每隔一段时间你就会得到一个必须处理大量线程流量的低级别类,然后你需要一个线程安全的类。我最喜欢的例子是JavaConcurrentSkipListMap,它与TreeMap的工作方式相同。每次使用JCSLM时,我都会使用TreeMap 100次,但是当我确实需要它时,它就是救星。 (没有它,我会坚持使用Java 1.4。)

如果你 要使一个线程安全的类,除了同步方法之外还要做一些事情。您可以在不需要时最终阻止其他线程。其他人可能会在您的类上同步,因此阻止不需要阻止的方法。至少同步一个纯粹用于同步的内部对象。更好的是,在类中的逻辑字段组上进行同步,并且只在需要它们的地方放置同步块。

还有其他方法可以在没有同步的情况下保持线程安全 - 不可变对象,比较和交换等。此外,启动其他线程以便主线程不被阻止可以避免严重的瓶颈,即使它增加了你机器的整体工作量。

那就是说,我运气好,在极少数情况下,只需同步一个班级中的每个方法。而且我可以(故意)通过在课堂上进行同步来锁定整个事物。它既简单又安全,如果轻松使用该课程也不会造成伤害。但我有时会猜错类的使用,并且在我的程序挂起时不得不放弃它,尽管有4个超线程内核,但只使用2%的CPU。

我或许应该补充一点,我的大部分工作都是时间关键的软件。但是,即使没有实际的死锁,过多的同步也会使任何程序运行速度太慢而无法实现任何目的。如果时间无关紧要,那么不应使用多个线程。