同步单例的正确使用?
-
13-09-2019 - |
题
所以我正在考虑建立一个业余爱好项目,这是一种临时性的事情,只是为了温习我的编程/设计。
它基本上是一个多线程网络蜘蛛,更新相同的数据结构object->int。
因此,为此使用数据库绝对是大材小用,我唯一能想到的是用于包含我的数据结构的线程安全单例。 http://web.archive.org/web/20121106190537/http://www.ibm.com/developerworks/java/library/j-dcl/index.html
我应该采用不同的方法吗?
解决方案
双检锁已被证明是不正确的,有缺陷的(如至少在Java中)。做搜索还是看维基百科的确切原因进入。
首先是程序的正确性。如果你的代码是不是线程安全的(在多线程环境下),则是坏了。正确性至上性能优化之前。
要正确,你必须同步整个getInstance
方法
public static synchronized Singleton getInstance() {
if (instance==null) ...
}
或静态初始化
private static final Singleton INSTANCE = new Singleton();
其他提示
在网络爬虫中对数据库使用延迟初始化可能不值得。延迟初始化增加了复杂性并持续影响速度。一种合理的情况是,很有可能永远不需要数据。此外,在交互式应用程序中,它可用于减少启动时间并提供 错觉 的速度。
对于像网络爬虫这样的非交互式应用程序来说,它肯定需要其数据库立即存在,延迟初始化不太合适。
另一方面,网络爬虫很容易并行化,并且将从多线程中受益匪浅。使用它作为练习来掌握 java.util.concurrent
图书馆将非常值得。具体看 ConcurrentHashMap
和 ConcurrentSkipListMap
, 这将允许多个线程读取和更新共享映射。
当你摆脱延迟初始化时,最简单的单例模式是这样的:
class Singleton {
static final Singleton INSTANCE = new Singleton();
private Singleton() { }
...
}
关键词 final
是这里的关键。即使您提供 static
单例的“getter”而不是允许直接字段访问,使得单例 final
有助于确保正确性并允许 JIT 编译器进行更积极的优化。
如果您的生命取决于几微秒,那么我建议您将资源锁定优化到真正重要的地方。
但在这种情况下,这里的关键字是 爱好项目!
这意味着如果您同步整个 获取实例() 方法 99.9% 的情况下你都会没事的。我不建议以任何其他方式这样做。
稍后,如果您通过分析证明 获取实例() 同步是你项目的瓶颈,那么你可以继续优化并发。但我真的怀疑这会给你带来麻烦。
杰奇!
尝试一下 比尔·皮尤 按需初始化的解决方案。该解决方案在不同的 Java 编译器和虚拟机上是最可移植的。该解决方案是线程安全的,不需要特殊的语言结构(即易失性和/或同步)。
http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh
约书亚布洛赫认为在他的著作“有效的Java第二版”我也同意,单个元素枚举类型是实现一个单身的最佳方式。
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;
}
}
请注意,单例现在是在安装静态字段期间构建的。这应该可行,并且不会面临潜在的错误同步的线程风险。
综上所述,也许您真正需要的是现代 JDK 中可用的线程安全数据结构之一。例如,我是 并发哈希映射:线程安全加上我不必编写代码(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;
}