我正在考虑创建一个类(如String,StringBuffer等)。这可以在单线程和多线程环境中使用。我不知道开发人员可能正在使用哪种环境。预计最坏的情况,我可以同步。
但是, 1.同步会影响性能。 2.没有同步,它不是线程安全的。
所以,我有两个选择。
我已经看到Java中的许多(如果不是全部。例如,ArrayList over Vector)类已经演变为采用第一种方法。在为我的班级决定这两个选项之前,我需要考虑哪些事项?
或者以不同的方式,我应该使用“public synchronized void bar()”而不是“public void bar()”只有当我知道肯定时才可以使用一个多线程环境,不应该同时运行?
编辑所以,显然我在标题中误用了“实用工具类”这个词。谢谢,Jon Skeet指出了这一点。我从标题中删除了世界“实用程序”。
举个例子,我在想一个类似Counter的课程。计数器就是一个例子。还有其他方法可以实现Counter。但这个问题是关于同步。 Counter对象跟踪某事已完成的次数。但它可以用于单线程或多线程环境。那么,我该如何处理Counter中的同步问题呢。
答案 0 :(得分:11)
我认为是一个实用程序类 - 通常是一个与模糊相关的公共静态方法的抓包 - 很少需要任何同步。除非班级保持一些可变的状态,否则你通常都会很好。
当然,如果你在线程之间共享自己共享的参数并且包含可变状态,你可能需要同步 - 但这通常应由调用者来决定。
如果你的意思是“实用类”,那么知道你的意思是很好的。如果它是一个没有可变状态的类(但可能是不可变的状态,在构造时设置)那么在线程之间共享通常很好。
如果它具有可变状态但是显式关于线程,我通常不会在该类中放置任何同步,而是记录它不线程安全。通常,调用者无论如何都需要使用多个对象同步多个操作,因此“一次一个方法”同步通常不会有帮助。
如果它是一个关于线程的类(例如管理生产者/消费者队列的东西),那么我会尝试使其成为线程安全的,但记录你的意思。我鼓励你不使这些方法本身同步,而是在私有的最终字段上进行同步,该字段只用于 用于同步;这样,您的类将包含 only 代码,该代码可能会在该对象上进行同步,从而更容易推断您的锁定。
您几乎肯定不根据性能做出这些决定。在大多数情况下,正确性和容易性远比性能更重要。
答案 1 :(得分:3)
关于您的上一个问题:如果可以从多个线程调用方法,则不会同步方法。如果方法使用某些可以从多个线程访问的状态,则可以同步该方法。
因此,即使只从一个线程调用bar()
方法,如果它通过多个线程访问通过其他方法读取和修改的实例变量,那么bar()
方法必须同步(或至少是访问共享变量的代码块)。同步完全是关于共享状态。
编辑:
关于您的主要问题:您可以简单地使用与集合框架相同的策略:使Counter成为一个接口,并提供默认的非线程安全实现。还提供了一个实用程序类(Counters
),其中包含一个返回同步Counter代理的方法:Counters.synchronizedCounter(Counter counter);
。
这样,你就拥有了两全其美的优势。请注意,此设计的一个重点是同步计数器本身是同步的。这使得调用者可以添加外部同步,以防计数器上的两个方法调用必须以原子方式完成:
Counter synchronizedCounter = Counters.synchronizedCounter(c);
// call a and b atomically:
synchronized (synchronizedCounter) {
synchronizedCounter.a();
synchronizedCounter.b();
}
答案 2 :(得分:1)
虽然同步性能得到了改善,但VM的其他部分也得到了改善。因此,同步仍然是一个明显的开销。
特别是,同步操作会阻止大量优化技巧。即使VM可以进行转义分析,VM仍然必须保留重新排序并添加内存障碍,以符合Java内存模型。
答案 3 :(得分:0)
现在,同步的“性能损失”非常小。如果您有证据表明同步导致了性能问题,那么您应该只关心它。
如果您的类通过多个方法引用的静态字段具有状态,则可能需要同步。在这种情况下,最好有实例字段并使用单例模式,这将更清楚地向其他程序员传达课程的意图。
答案 4 :(得分:0)
在现代JVM上,单线程访问同步方法的性能损失几乎可以忽略不计。
但为什么不创建基准并自己看看呢?