我认为我已经实现了Double-checked锁定模式,但不确定它是否安全或是否按预期工作。实现相同的任何其他逻辑都会非常有用。
public class OnProperties {
private static String dfltPropertyFile = "on.properties";
private static long refreshSecs = 120L;
private static Properties props;
private static long lastReadTimestamp = 0;
public static String getProperty(String propertyName, String dfltValue) {
long currentTimestamp = System.currentTimeMillis() / 1000L;
if (props == null
|| (refreshSecs > 0 && (currentTimestamp - lastReadTimestamp) > refreshSecs)) {
synchronized (props) {
if (props == null
|| (refreshSecs > 0 && (currentTimestamp - lastReadTimestamp) > refreshSecs)) {
lastReadTimestamp = currentTimestamp;
try {
loadProperties(dfltPropertyFile);
refreshSecs = getProperty("on.properties.refresh", 120L);
if (refreshSecs < 0L) {
refreshSecs = 0L;
}
} catch (Exception e) {
refreshSecs = 600L;
}
}
}
}
if (props == null) {
return dfltValue;
}
String propertyValue = props.getProperty(propertyName, dfltValue);
return propertyValue;
}
public static boolean getProperty(String propertyName, boolean dfltValue) {
boolean value = dfltValue;
String strValue = getProperty(propertyName, (String) null);
if (strValue != null) {
try {
value = Boolean.parseBoolean(strValue);
} catch (NumberFormatException e) {
// just keep the default
}
}
return value;
}
private static void loadProperties(String p_propertiesFile)
throws java.io.IOException, java.io.FileNotFoundException {
InputStream fileStream = new FileInputStream(p_propertiesFile);
props = new Properties();
props.load(fileStream);
fileStream.close();
}
}
通常,多个运行的线程经常访问“getProperty”方法,如下所示:
extDebug = OnProperties.getProperty("on.extdebug", false);
答案 0 :(得分:1)
原子值保证始终将完整的最新值返回给所有线程。在这种情况下,这可以防止许多多线程问题。仍然需要一点同步,但可以将其限制到最小。请参阅下面的实施:
import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
public class OnProperties {
private static int refreshIntervalDefaultSecs;
private static int refreshIntervalOnErrorSecs;
static {
setRefreshInterval(120);
}
private static final AtomicReference<Properties> propsRef = new AtomicReference<Properties>(new Properties());
private static final AtomicLong nextPropsLoad = new AtomicLong(0L);
private static final Object loadLock = new Object();
private static String dfltPropertyFile = "on.properties";
public static String getProperty(String key, String defaultValue) {
String value = getProperty(key);
if (value == null) {
value = defaultValue;
}
return value;
}
private static String getProperty(String key) {
reloadWhenNeeded();
return propsRef.get().getProperty(key);
}
private static void reloadWhenNeeded() {
long now = System.currentTimeMillis();
if (now > nextPropsLoad.get()) {
boolean reload = false;
synchronized(loadLock) {
if (now > nextPropsLoad.get()) {
// need loadLock because there is time between previous get()
// and next set()
updateNextPropsLoad(now, refreshIntervalDefaultSecs);
reload = true;
}
}
if (reload) {
reloadProps(now);
}
}
}
private static void updateNextPropsLoad(long now, int nextRefreshSecs) {
nextPropsLoad.set(now + nextRefreshSecs * 1000);
}
private static void reloadProps(long now) {
Properties p = new Properties();
FileInputStream in = null;
System.out.println("Reloading from " + new File(dfltPropertyFile).getAbsolutePath());
try {
p.load(in = new FileInputStream(new File(dfltPropertyFile)));
propsRef.set(p);
setRefreshInterval(getProperty("on.properties.refresh", 120));
updateNextPropsLoad(now, refreshIntervalDefaultSecs);
} catch (Exception e) {
updateNextPropsLoad(now, refreshIntervalOnErrorSecs);
} finally {
try { if (in != null) in.close(); } catch (Exception e) {
updateNextPropsLoad(now, refreshIntervalOnErrorSecs);
}
}
}
private static void setRefreshInterval(int refreshSecs) {
if (refreshSecs < 1) {
refreshSecs = 120;
}
refreshIntervalDefaultSecs = refreshSecs;
refreshIntervalOnErrorSecs = 5 * refreshSecs;
}
public static boolean getProperty(String key, boolean defaultValue) {
boolean value = defaultValue;
String svalue = getProperty(key);
if (svalue != null) {
try {
value = Boolean.valueOf(svalue);
} catch (Exception ignored) {}
}
return value;
}
public static int getProperty(String key, int defaultValue) {
int value = defaultValue;
String svalue = getProperty(key);
if (svalue != null) {
try {
value = Integer.valueOf(svalue);
} catch (Exception ignored) {}
}
return value;
}
public static void main(String[] args) {
System.out.println("Refresh value from file: " + getProperty("on.properties.refresh", 120));
System.out.println("No reload " + getProperty("does.not.exist", true));
System.out.println("Next reload after " + ((nextPropsLoad.get() - System.currentTimeMillis()) / 1000) + " seconds.");
}
}
实现的一个缺点是,当选择从文件重新加载属性时,一个线程将变慢。更好的方法是创建一个'监视程序'线程/计划任务,检查每个(例如)五秒钟,如果属性文件具有更改的修改日期,然后触发重新加载(在这种情况下,属性的AtomicReference仍然会出现派上用场)。
还要记住,存在逻辑线程问题:如果属性值是相互关联的(即,如果另一个值也被更新,则一个值只是正确的),重新加载可能会呈现具有旧值和新值的线程,混合。唯一的方法是在使用属性的相关值的方法中保持对一组属性的引用(在这种情况下,使用静态方法和变量这样的类不方便)。
答案 1 :(得分:0)
这是不安全的,因为你有多个变量以非线程安全的方式读取(即访问不同步且它们不易变)。
看起来工作流程主要是通过一些写入来读取。我建议使用ReentrantReadWriteLock来同步访问权限。
答案 2 :(得分:0)
要使双重检查锁定正常工作,您必须做两件事:
private static Properties props
必须声明为volatile;
private static final Object propsLockObject = new Object();
...
synchronized(propsLockObject) {
...
P.S。除非声明为volatile,否则lastReadTimestamp
也不会起作用。虽然这不再是双重检查锁定。
答案 3 :(得分:0)
完成后,您的代码将按照您希望的方式运行。
public class OnProperties {
private static String dfltPropertyFile = "on.properties";
private static long refreshSecs = 120L;
private static Properties props = new Properties();
private static long lastReadTimestamp = 0;
public static String getProperty(String propertyName, String dfltValue) {
long currentTimestamp = System.currentTimeMillis() / 1000L;
if (refreshSecs > 0 && (currentTimestamp - lastReadTimestamp) > refreshSecs) {
synchronized (props) {
if (refreshSecs > 0 && (currentTimestamp - lastReadTimestamp) > refreshSecs) {
lastReadTimestamp = currentTimestamp;
try {
loadProperties(dfltPropertyFile);
refreshSecs = getProperty("on.properties.refresh", 120L);
if (refreshSecs < 0L) {
refreshSecs = 0L;
}
} catch (Exception e) {
refreshSecs = 600L;
}
}
}
}
String propertyValue = props.getProperty(propertyName, dfltValue);
return propertyValue;
}
public static boolean getProperty(String propertyName, boolean dfltValue) { boolean value = dfltValue;
String strValue = getProperty(propertyName, (String) null);
if (strValue != null) {
try {
value = Boolean.parseBoolean(strValue);
} catch (NumberFormatException e) {
// just keep the default
}
}
return value;
}
private static void loadProperties(String p_propertiesFile) throws java.io.IOException, java.io.FileNotFoundException { InputStream fileStream = new FileInputStream(p_propertiesFile); props.load(fileStream); fileStream.close(); } }
答案 4 :(得分:0)
请接受双重检查的锁定习惯用法被破坏且不起作用(即不能正确同步)。即使你使用volatile
(在正确的地方)使其工作,它也太复杂了。
所以我的建议是:简单地同步一切。然后尝试衡量。如果您发现OnProperties
是瓶颈,请考虑更强大/更聪明的同步技术,并在必要时返回:
public class OnProperties {
/* some private fields here */
public static synchronized String getProperty(String propertyName, String dfltValue) {
reloadPropertiesIfNecessary();
return props.getProperty(propertyName, dfltValue);
}
/* other public methods using getProperty come here */
private static void reloadPropertiesIfNecessary() {
// check timestamp etc.
if (/* check timestamp etc. */) {
loadProperties(dfltPropertyFile);
// update timestamp etc.
}
}
private static void loadProperties(String filename) throws IOException {
try (InputStream stream = new FileInputStream(filename)) {
props = new Properties();
props.load(fileStream);
}
}
}