所以我正在考虑建立一个业余爱好项目,一种特殊的东西,只是为了复习我的编程/设计。
它基本上是一个多线程网络蜘蛛,更新相同的数据结构 object->int。
因此,为此使用数据库绝对是矫枉过正,而我唯一能想到的就是用于包含我的数据结构的线程安全单例。http://web.archive.org/web/20121106190537/http://www.ibm.com/developerworks/java/library/j-dcl/index.html
我应该研究另一种方法吗?
所以我正在考虑建立一个业余爱好项目,一种特殊的东西,只是为了复习我的编程/设计。
它基本上是一个多线程网络蜘蛛,更新相同的数据结构 object->int。
因此,为此使用数据库绝对是矫枉过正,而我唯一能想到的就是用于包含我的数据结构的线程安全单例。http://web.archive.org/web/20121106190537/http://www.ibm.com/developerworks/java/library/j-dcl/index.html
我应该研究另一种方法吗?
双重检查锁定已被证明是不正确且有缺陷的(至少在 Java 中如此)。搜索或查看Wikipedia 的条目以了解确切原因。
首先是程序的正确性。如果您的代码不是线程安全的(在多线程环境中),那么它就会损坏。在性能优化之前,正确性是第一位的。
为了正确,您必须同步整个getInstance
方法
public static synchronized Singleton getInstance() {
if (instance==null) ...
}
或静态初始化它
private static final Singleton INSTANCE = new Singleton();
在网络爬虫中对数据库使用延迟初始化可能不值得。延迟初始化增加了复杂性和持续的速度冲击。一种合理的情况是很有可能永远不需要数据。此外,在交互式应用程序中,它可用于减少启动时间并产生速度错觉。
对于像网络爬虫这样的非交互式应用程序,它肯定需要它的数据库立即存在,延迟初始化是不合适的。
另一方面,网络爬虫很容易并行化,并且会从多线程中受益匪浅。用它作为练习来掌握java.util.concurrent
图书馆将是非常值得的。具体来说,查看ConcurrentHashMap
and ConcurrentSkipListMap
,它将允许多个线程读取和更新共享映射。
当你摆脱惰性初始化时,最简单的单例模式是这样的:
class Singleton {
static final Singleton INSTANCE = new Singleton();
private Singleton() { }
...
}
关键字final
是这里的关键。即使您为static
单例提供“getter”而不是允许直接访问字段,但创建单例final
有助于确保正确性并允许 JIT 编译器进行更积极的优化。
试试Bill Pugh的按需初始化持有人习语的解决方案。该解决方案是跨不同 Java 编译器和虚拟机的最可移植的解决方案。该解决方案是线程安全的,不需要特殊的语言结构(即易失性和/或同步)。
http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh
如果您的生命依赖于几微秒,那么我建议您将资源锁定优化到真正重要的地方。
但在这种情况下,这里的关键字是爱好项目!
这意味着,如果您同步了整个getInstance()方法,那么在 99.9% 的情况下都可以。我不建议以任何其他方式这样做。
稍后,如果您通过 profiling 证明getInstance()同步是您项目的瓶颈,那么您可以继续优化并发性。但我真的怀疑它会给你带来麻烦。
杰奇!
正如 Joshua Bloch 在他的《有效的 java 2nd edition》一书中所说,我也同意单元素枚举类型是实现单例的最佳方式。
public enum Singleton {
INSTANCE;
public void doSomething() { ... }
}
为什么不创建一个作为依赖注入传递给每个线程的数据结构。这样你就不需要单例了。您仍然需要使线程安全。
如果您查看该文章的最底部,您会看到仅使用静态字段的建议。那将是我的倾向:您实际上并不需要惰性实例化(因此您不需要getInstance()
同时成为访问器和工厂方法)。您只想确保您拥有这些东西中的一件,而且只有一件。如果你真的需要全局访问这样的事情,我会在最底部使用该代码示例:
class Singleton
{
private Vector v;
private boolean inUse;
private static Singleton instance = new Singleton();
private Singleton()
{
v = new Vector();
inUse = true;
//...
}
public static Singleton getInstance()
{
return instance;
}
}
请注意,Singleton 现在是在安装静态字段期间构建的。这应该有效,并且不会面临潜在的错误同步的线程风险。
综上所述,也许您真正需要的是现代 JDK 中可用的线程安全数据结构之一。例如,我是ConcurrentHashMap的忠实粉丝:线程安全加上我不必编写代码(FTW!)。
您引用的文章仅讨论了如何创建单例对象,在这种情况下可能是一个集合,是线程安全的。您还需要一个线程安全的集合,以便集合操作也能按预期工作。确保单例中的基础集合是同步的,可能使用ConcurrentHashMap。
查看这篇文章在 C# 中实现单例模式
public sealed class Singleton
{
Singleton()
{
}
public static Singleton Instance
{
get
{
return Nested.instance;
}
}
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
怎么样:
public static Singleton getInstance() {
if (instance == null) {
synchronize(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}