我们已将所有遗留代码打包到一个库中,新版本的代码会在需要时调用遗留代码;虽然这种方法很好,但目前我们处于困扰之中,因为遗留代码的一部分有线程不安全的单例,而调用它们的新代码期望它们是线程安全的;我们负担不起同步块,因为当负载超过一定数量时会阻塞系统。思想会接受你的意见。谢谢!
修改 这些单例是懒惰的,没有对null实例进行同步和双重检查:
public static Parser getInstance() {
Parser p = null;
try {
if (instance == null) {
instance = new Parser(...);
}
} catch (Exception x) {
...
}
return p;
}
并且此代码至少已有8年的历史,我们无法修复它们。
答案 0 :(得分:0)
正如上面评论中提到的,你应该简单地修复这些类(这是一个简单的修复!)。也就是说,假设您无法触及此代码,您可以继承它并“覆盖”(实际上它被称为“隐藏”,因为该方法是静态的)getInstance()
。这只会修复损坏的部分,并且会为其他部分保持相同的逻辑。
顺便说一句,如果您决定使用双重空检查来实现单例,那么您不仅必须同步最里面的检查,还必须将instance
声明为volatile
。
有更好的方法来实现懒惰和急切的单例(静态类,内部助手类和枚举),请确保在选择之前评估所有选项。
答案 1 :(得分:0)
如果你有一个非线程安全的对象,并且工厂方法返回一个单例,你别无选择,只能进行同步。
您需要更改构造新对象的工厂方法(或创建一个新方法,如果您不能编辑原始代码)。如果这太昂贵了(并且不要假设它是,直到你测试它),看看哪些方面是昂贵的,也许一些对象的依赖关系仍然可以是单例。
这里没有神奇的解决方案。
但是......我即将告诉你我在类似情况下做过的黑客行为。但这是最后的度假村,无论如何可能无法正常工作。曾经有一个Web应用程序由许多servlet组成,这些servlet包含有效的全局和局部变量,作为成员变量。写它的那个人并没有意识到servlet的成员是单个实例。该应用程序使用1个客户端进行测试,但多个连接失败。我们需要快速修复。我让servlet按照自己的意思构建自己。当调用doGet和doPost方法时,我得到了servlet来克隆自己并将请求传递给克隆。这复制了"全球"成员并向请求提出了新的未初始化成员的请求。但是,它有问题。所以不要这样做。只需修复你的代码。 :)