如何解决Volatile 和 Synchronized 以解决竞争条件:单例成员字段
我在尝试理解和修复 Fortify 扫描报告的错误时遇到了一些问题。我有这门课:
public class DaoImpl extends BaseDaoImpl {
private static volatile String sNric;
synchronized private void setInfo(InfoTO pers) {
sNric = pers.getNRIC();
}
synchronized public InfoTO getInfo() {
InfoTO pers = new InfoTO();
sNric = retrieveDetail();
pers.setNRIC(sNric);
}
synchronized private String retrieveDetail() {
// some logic to get info from database
}
}
我的代码最初没有 static volatile
和 synchronized
关键字。 Fortify 在 Race Condition: Singleton Member Field
和 sNric
sNric = retrieveDetail();
警告
我进行了研究,发现了 this solution。但是,我对 volatile 和 synchronized 的概念不是很确定。上述建议的解决方案是否会导致一些死锁问题?
解决方法
volatile
与 synchronized
的“概念”是您可能不应该这样做。
如果您在所有访问和更新共享变量(例如您的 synchronized
变量)的方法中使用 sNric
,那么声明 volatile
是多余且低效的。
至于您关于死锁的问题,我看不出有任何方法可以仅根据上面的代码获得死锁。 但是,您没有向我们展示 InfoTO
的代码或使用这些类的代码。涉及DaoImpl
实例锁和其他锁的死锁并非不可能发生。
如果您担心 getInfo
调用 this.retrieveDetail
可能会死锁。这里只涉及一个(DaoImpl
实例)锁,并且 Java 原始锁是可重入的。 (如果一个线程试图获取一个它已经持有的原始锁,它不会被阻塞。)
最后,如果您担心线程安全,请检查 setNRIC
和 getNRIC
是否是线程安全的。如果不是,我认为上述内容不能安全地处理 InfoTo
对象。
请注意,除非您考虑到它所依赖的其他类以及它使用/打算使用的方式,否则您无法推断类的线程安全性。
,您没有真正为某个答案提供足够的信息和代码详细信息。但我会试一试。
AtomicReference
共享变量的线程安全是关于状态转换的可见性和原子性。
我通常更喜欢使用 the Atomic…
classes 来解决可见性和原子性问题。这些类可以替代 volatile
和 synchronized
。
在这种情况下,我们可以使用 AtomicReference
类将您当前所需的 String
值的引用作为其有效负载。请注意,我们将它标记为 final
作为对 AtomicReference
对象本身的引用永远不会改变。它的有效负载,一个引用(指针)将我们带到所需的 String
对象,确实发生了变化。在某一时刻,它可能指向 String
值 "dog"
,而稍后它可能指向 String
值 "cat"
。但是 String
的容器始终是完全相同的 AtomicReference
对象,是包含文本的包装器,包含 String
对象。
如果您的 InfoTO
类如下所示:
package work.basil.example;
public class InfoTO
{
private String nric ;
public String getNric ( ) { return this.nric; }
public void setNric ( String nric ) { this.nric = nric; }
}
...那么您的 DaoImpl
可能看起来像这样:
package work.basil.example;
import java.util.concurrent.atomic.AtomicReference;
public class DaoImpl
{
private AtomicReference < String > sNric;
private void setInfo ( InfoTO pers )
{
this.sNric.set( pers.getNric() );
}
public InfoTO getInfo ( )
{
InfoTO pers = new InfoTO();
String s = retrieveDetail();
pers.setNric( s );
return pers ;
}
private String retrieveDetail ( )
{
return this.sNric.get();
}
}
你的台词:
sNric = retrieveDetail();
pers.setNRIC(sNric);
……对我来说没有意义。您使用一个字段来保存临时值。所以我用一个局部变量代替。
你的 retrieveDetail
方法对我来说毫无意义。您似乎正在从对数据库的调用返回与您在字段 sNric
中缓存的字符串值完全相同的字符串值。因此,我更改了该方法以访问缓存字段 sNric
。这似乎更符合您的预期逻辑,更重要的是,显示了 AtomicReference
的 getter 和 setter 正在运行。
当然,正如其他人所说,您可能在没有向我们展示的大量代码中存在其他线程安全问题。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。