我的 getStatement 方法线程安全吗?
Is my getStatement method thread safe?
我有一个下面的 Singleton class,在我的 getStatement
方法中,我通过执行 if 检查来填充 CHM。
public class CacheHolder {
private static final Map<String, PreparedStatement> holder = new ConcurrentHashMap<>();
private static class Holder {
private static final CacheHolder INSTANCE = new CacheHolder();
}
public static CacheHolder getInstance() {
return Holder.INSTANCE;
}
private CacheHolder() {}
public BoundStatement getStatement(String cql) {
Session session = TestUtils.getInstance().getSession();
PreparedStatement ps = holder.get(cql);
if (ps == null) {
ps = session.prepare(cql);
holder.put(cql, ps);
}
return ps.bind();
}
}
我的 getStatement
方法线程安全吗?
参考@Bandi Kishore 的答案,因为它比下面的答案更有效(下面的答案需要 synchronization
每次调用 getStatement()
可以通过再添加一个 null
检查).
Is my getStatement method thread safe?
不,它不是线程安全的,在您的 getStatement(String cql)
方法中,您正在执行 null
检查竞争条件 ,这通常被称为 双重检查锁定,你可以看看here。即,当线程正在执行 holder.get(cql);
时,您的代码中存在竞争条件,您需要 synchronize
该代码的关键部分,如下所示:
public static BoundStatement getStatement(String cql) {
PreparedStatement ps = null;
Session session = null;
try {
session = TestUtils.getInstance().getSession();
synchronized {
PreparedStatement ps = holder.get(cql);
if (ps == null) {
ps = session.prepare(cql);
holder.put(cql, ps);
}
}
} finally {
//release the resources
}
return ps.bind();
}
此外,作为旁注,请确保您正在释放资源。
@javaguy 提供的答案是正确的,但只是一个小的优化,以确保在不需要时不会为每个线程执行同步块。
public static BoundStatement getStatement(String cql) {
PreparedStatement ps = null;
Session session = null;
try {
session = TestUtils.getInstance().getSession();
PreparedStatement ps = holder.get(cql);
if(ps == null) { // If PS is already present in cache, then we don't have to synchronize and make threads wait.
synchronized {
ps = holder.get(cql);
if (ps == null) {
ps = session.prepare(cql);
holder.put(cql, ps);
}
}
}
} finally {
//release the resources
}
return ps.bind();
}
您也可以使用Guava Cache or if you want a Map then Guava MapMaker。
使用 Guava 缓存:
LoadingCache<String, PreparedStatement> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(
new CacheLoader<String, PreparedStatement>() {
public PreparedStatement load(String cql) throws Exception {
return createPreparedStatement(cql);
}
});
使用地图制作工具:
ConcurrentMap<String, PreparedStatement> cache = new MapMaker()
.concurrencyLevel(32)
.weakValues()
.makeComputingMap(
new Function<String, PreparedStatement>() {
public PreparedStatement apply(String cql) {
return createPreparedStatement(cql);
}
});
此外,我建议不要缓存 PreparedStatement 的,因为这些资源需要释放 AFAIK。
我有一个下面的 Singleton class,在我的 getStatement
方法中,我通过执行 if 检查来填充 CHM。
public class CacheHolder {
private static final Map<String, PreparedStatement> holder = new ConcurrentHashMap<>();
private static class Holder {
private static final CacheHolder INSTANCE = new CacheHolder();
}
public static CacheHolder getInstance() {
return Holder.INSTANCE;
}
private CacheHolder() {}
public BoundStatement getStatement(String cql) {
Session session = TestUtils.getInstance().getSession();
PreparedStatement ps = holder.get(cql);
if (ps == null) {
ps = session.prepare(cql);
holder.put(cql, ps);
}
return ps.bind();
}
}
我的 getStatement
方法线程安全吗?
参考@Bandi Kishore 的答案,因为它比下面的答案更有效(下面的答案需要 synchronization
每次调用 getStatement()
可以通过再添加一个 null
检查).
Is my getStatement method thread safe?
不,它不是线程安全的,在您的 getStatement(String cql)
方法中,您正在执行 null
检查竞争条件 ,这通常被称为 双重检查锁定,你可以看看here。即,当线程正在执行 holder.get(cql);
时,您的代码中存在竞争条件,您需要 synchronize
该代码的关键部分,如下所示:
public static BoundStatement getStatement(String cql) {
PreparedStatement ps = null;
Session session = null;
try {
session = TestUtils.getInstance().getSession();
synchronized {
PreparedStatement ps = holder.get(cql);
if (ps == null) {
ps = session.prepare(cql);
holder.put(cql, ps);
}
}
} finally {
//release the resources
}
return ps.bind();
}
此外,作为旁注,请确保您正在释放资源。
@javaguy 提供的答案是正确的,但只是一个小的优化,以确保在不需要时不会为每个线程执行同步块。
public static BoundStatement getStatement(String cql) {
PreparedStatement ps = null;
Session session = null;
try {
session = TestUtils.getInstance().getSession();
PreparedStatement ps = holder.get(cql);
if(ps == null) { // If PS is already present in cache, then we don't have to synchronize and make threads wait.
synchronized {
ps = holder.get(cql);
if (ps == null) {
ps = session.prepare(cql);
holder.put(cql, ps);
}
}
}
} finally {
//release the resources
}
return ps.bind();
}
您也可以使用Guava Cache or if you want a Map then Guava MapMaker。
使用 Guava 缓存:
LoadingCache<String, PreparedStatement> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(
new CacheLoader<String, PreparedStatement>() {
public PreparedStatement load(String cql) throws Exception {
return createPreparedStatement(cql);
}
});
使用地图制作工具:
ConcurrentMap<String, PreparedStatement> cache = new MapMaker()
.concurrencyLevel(32)
.weakValues()
.makeComputingMap(
new Function<String, PreparedStatement>() {
public PreparedStatement apply(String cql) {
return createPreparedStatement(cql);
}
});
此外,我建议不要缓存 PreparedStatement 的,因为这些资源需要释放 AFAIK。