我们的代码库中目前有一个类,它在方法级别使用
synchronized
关键字来确保多线程操作中的数据一致性。 它看起来像这样:
public class Foo
{
public synchronized void abc() { ... }
public synchronized void def() { ... }
//etc.
}
这样做的好处是任何使用该类的人都可以免费获得同步。 当您创建
Foo
的实例时,您不必记住在 synchronized
块或类似内容内访问它。
不幸的是,方法级别的同步似乎不再能解决问题了。 相反,我们必须开始同步
Foo
本身。 我认为像java.util.concurrent.AtomicReference
这样的东西也不会削减它。 我想确保在进行特定(可能有点长)操作时没有其他人接触 Foo
的实例。 所以现在我们将在代码中包含这样的块:
Foo foo = new Foo(); //this got created somewhere
//somewhere else entirely
synchronized(foo)
{
//do operation on foo
foo.doStuff();
foo.doOtherStuff();
}
所以我主要担心的是我和一些开发人员共享这段代码。
Foo
对象相当普遍。 由于我们不再在方法级别免费获得同步,因此我们必须始终记住访问Foo
块中的synchronized
对象。
所以我的问题是,如果在
Foo
块之外访问 synchronized
的实例,Java 中是否有任何机制(内置或第三方)允许我在编译时生成警告或错误?
理想情况下,我可以对类声明执行一些操作(如下例所示):
@EnsureSynchronized
public class Foo
{
//etc.
}
或者当我声明
Foo
的实例时我可以做的事情(下面的例子):
@EnsureSynchronized
private Foo foo;
我知道如果我真的愿意,我可能可以编写自定义 FindBugs 或 PMD 规则来执行此操作,但我希望这样的东西已经存在。
所以我问你,SO社区,如果你处于这种情况,你会如何确保
Foo
对象只能在synchronized
块内访问和修改?
Findbugs 非常擅长查找不一致的同步,因此只要您有一些代码可以同步对对象的所有访问,并运行 findbugs,它应该会提醒您同步失败。
与此错误模式匹配的典型错误是忘记同步旨在实现线程安全的类中的方法之一。
您可以选择标记为“未同步访问”的节点来显示检测器认为未同步访问字段的代码位置。
请注意,该检测器存在多种不准确性来源;例如,检测器无法静态地检测所有持有锁的情况。 此外,即使检测器能够准确地区分锁定访问和未锁定访问,相关代码也可能仍然是正确的。
net.jcip.annotations.NotThreadSafe
进行注释。
来自 第 10 章注释 :
FindBugs 还支持以下注释:
- ...
net.jcip.annotations.NotThreadSafe
如果您希望在编译时进行检查,FindBugs 和 PMD 就不行。我建议使用 Java 的注释处理工具(APT)。它将允许您创建一个自定义注释处理器,该处理器可以向编译过程添加对注释类的使用的检查,并在不满足同步要求时导致编译器警告或错误。事实上,您甚至可以使用它篡改代码以在编译期间添加同步(如果尚不存在)。
要使用您创建的注释处理器,您只需在编译项目时确保它位于类路径上。无需额外的自动分析。
如果您调用
notify()
时没有同步块或正在同步的方法,您将得到一个 IllegalMonitorStateExcept
(请参阅 文档)。然而,这样做是非常困难的,如果有的话,应该只用于调试而不是在生产环境中。