9

我正在考虑创建一个类(如 String、StringBuffer 等)。这可以在单线程和多线程环境中使用。我不知道开发人员可能会使用哪种环境。预计最坏的情况,我可以同步。

但是,1. 同步会影响性能。2.没有同步,就不是线程安全的。

所以,我有两个选择。

  1. 保持类不同步 - 但使用此类的开发人员需要在适当的时候对其进行同步。
  2. 拥有所有同步方法 - 并受到性能影响。

我已经看到 Java 中的许多(如果不是全部的话。例如,ArrayList over Vector)类已经演变为采用第一种方法。在为我的班级决定这两个选项之前,我需要考虑哪些事情?

或者换一种说法,我是否应该使用“public synchronized void bar()”而不是“public void bar()”,只有当我确定bar 可以在多线程环境中使用并且不应该运行时同时?

编辑 所以,很明显我在标题中误用了“实用程序类”这个词。谢谢 Jon Skeet 指出这一点。我已经从标题中删除了世界“实用程序”。

举个例子,我在考虑一个类,比如 Counter。计数器只是一个例子。还有其他方法可以实现 Counter。但是这个问题是关于同步的。Counter 对象会记录某事已经完成了多少次。但它可能用于单线程或多线程环境。那么,我应该如何处理 Counter 中的同步问题。

4

5 回答 5

11

我认为是一个实用程序——通常是一个模糊相关的公共静态方法的抓包——很少需要任何同步。除非该类保持一些可变状态,否则您通常绝对没问题。

当然,如果您采用本身在线程之间共享并包含可变状态的参数,您可能需要同步 - 但这通常应该由调用者决定。

如果您通过“实用程序类”表示其他意思,那么最好知道您的意思。如果它是一个没有可变状态的类(但可能是不可变状态,在构造时设置),那么通常可以在线程之间共享。

如果它具有可变状态但没有明确关于线程,我通常不会在该类中放置任何同步,但记录它不是线程安全的。通常,调用者无论如何都需要使用多个对象来同步多个操作,因此“一次方法”同步通常没有帮助。

如果它是一个关于线程的类(例如管理生产者/消费者队列的东西),那么我会尝试使它成为线程安全的,但记录你的意思。我鼓励您不要让方法本身同步,而是在用于同步的私有 final 字段上同步;这样,您的类将包含唯一可能在该对象上同步的代码,从而更容易推理您的锁定。

您几乎可以肯定应该根据性能做出这些决定。在大多数情况下,正确性和易用性远比性能重要。

于 2012-11-01T20:19:19.550 回答
3

关于您的最后一个问题:如果可以从多个线程调用方法,则您不会同步该方法。如果方法使用可以从多个线程访问的某种状态,则您同步方法。

因此,即使您的bar()方法仅从一个线程调用,如果它访问一个由多个线程通过其他方法读取和修改的实例变量,那么该bar()方法必须是同步的(或者至少是访问的代码块共享变量)。同步是关于共享状态的。

编辑:

关于您的主要问题:您可以简单地使用与集合框架相同的策略:使您的 Counter 成为接口,并提供默认的非线程安全实现。还提供一个实用程序类 ( Counters),其中包含一个返回同步计数器代理的方法:Counters.synchronizedCounter(Counter counter);

这样,您就可以两全其美了。请注意,此设计的一个重点是同步计数器自身同步。这使得调用者可以添加外部同步,以防计数器上的两个方法调用必须以原子方式完成:

Counter synchronizedCounter = Counters.synchronizedCounter(c);
// call a and b atomically:
synchronized (synchronizedCounter) {
    synchronizedCounter.a();
    synchronizedCounter.b();
}
于 2012-11-01T20:26:34.413 回答
1

虽然同步性能得到了改进,但 VM 的其他部分也得到了改进。因此同步仍然是一个明显的开销。

特别是,同步操作会阻止许多优化技巧。即使 VM 可以进行逃逸分析,VM 仍然必须保留重新排序并添加内存屏障,以符合 Java 内存模型。

于 2012-11-01T20:39:55.567 回答
0

如今,同步带来的“性能损失”非常小。只有当您有证据表明同步导致性能问题时,您才应该关注它。

如果您的类通过多个方法引用的静态字段具有状态,则您可能需要同步。在这种情况下,最好有实例字段并使用单例模式,这将更清楚地向其他程序员传达类的意图是什么。

于 2012-11-01T20:21:52.920 回答
0

在现代 JVM 上,单线程访问同步方法的性能损失几乎可以忽略不计。

但是为什么不创建一个基准并亲自看看呢?

于 2012-11-01T20:22:13.420 回答