我正在尝试实现锁定,通过该锁定我想避免在对三个映射进行写操作时发生读取。所以我的要求是-
因为我有三个Maps-
primaryMapping
,secondaryMapping
和tertiaryMapping
,所以它应该返回三个更新后的 map 的所有新值,或者应该返回 map 的所有旧值。基本上,在更新时,我不想返回具有旧值的primaryMapping
,具有新值的secondaryMapping
和具有新值的tertiaryMapping
。它应该是一致的,要么应该返回旧值,要么应该在更新 map 后返回新值。就我而言, map 的更新将每三个月或四个月进行一次。
下面是我的
ClientData
类,在其中我使用的是整个逻辑都存在的ReentrantLock
-public class ClientData {
private static final class MapContainer {
private Map<String, Map<Integer, String>> value = null;
public Map<String, Map<Integer, String>> getValue() {
return value;
}
public void setValue(Map<String, Map<Integer, String>> value) {
this.value = value;
}
}
private static final MapContainer primaryMapping = new MapContainer();
private static final MapContainer secondaryMapping = new MapContainer();
private static final MapContainer tertiaryMapping = new MapContainer();
private static final MapContainer[] containers = {primaryMapping, secondaryMapping, tertiaryMapping};
private static boolean allset = false;
private static final Lock lock = new ReentrantLock();
private static final Condition allsetnow = lock.newCondition();
private static final Map<String, Map<Integer, String>> getMapping(MapContainer container) {
lock.lock();
try {
while (!allset) {
allsetnow.await();
}
return container.getValue();
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // reset interruptedd state.
throw new IllegalStateException(e);
} finally {
lock.unlock();
}
}
public static void setAllMappings(Map<String, Map<Integer, String>> primary,
Map<String, Map<Integer, String>> secondary,
Map<String, Map<Integer, String>> tertiary) {
lock.lock();
try{
// how to avoid this?
if (allset) {
throw new IllegalStateException("All the maps are already set");
}
primaryMapping.setValue(primary);
secondaryMapping.setValue(secondary);
tertiaryMapping.setValue(tertiary);
allset = true;
allsetnow.signalAll();
} finally {
lock.unlock();
}
}
public static Map<String, Map<Integer, String>> getPrimaryMapping() {
return getMapping(primaryMapping);
}
public static Map<String, Map<Integer, String>> getSecondaryMapping() {
return getMapping(secondaryMapping);
}
public static Map<String, Map<Integer, String>> getTertiaryMapping() {
return getMapping(tertiaryMapping);
}
}
下面是我的后台线程代码,它将从我的服务URL中获取数据,并在我的应用程序启动后每10分钟保持运行,然后它将解析来自该URL的数据并将其存储在
ClientData
类变量中在那三张 map 中。public class TempScheduler {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public void startScheduler() {
final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
callServers();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}, 0, 10, TimeUnit.MINUTES);
}
}
// call the servers and get the data and then parse
// the response.
private void callServers() {
String url = "url";
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
parseResponse(response);
}
// parse the response and store it in a variable
private void parseResponse(String response) {
//...
ConcurrentHashMap<String, Map<Integer, String>> primaryTables = null;
ConcurrentHashMap<String, Map<Integer, String>> secondaryTables = null;
ConcurrentHashMap<String, Map<Integer, String>> tertiaryTables = null;
//...
// store the data in ClientData class variables if anything has changed
// which can be used by other threads
if(changed) {
ClientData.setAllMappings(primaryTables, secondaryTables, tertiaryTables);
}
}
}
我将在我的主要应用程序的主线程中使用
getPrimaryMapping
类中的getSecondaryMapping
,getTertiaryMapping
和ClientData
,因此我想返回这三个映射中的所有新值集,或者如果发生更新,则阻止它并返回所有更新完成后,这三个 map 的新值集。问题陈述:-
在上面的
ClientData
类中所示的代码库中,我想,第一次设置后,我将无法更新 map ,因为此行会引起问题,并且会引发异常,然后引发异常实现我的第二点,如上所示?// how to avoid this?
if (allset) {
throw new IllegalStateException("All the maps are already set");
}
如何成功实现以上两点?我想,这里有些不重要的事情我想念吗?我想在这里使用ReentrantLock,但也欢迎其他任何建议。我主要关心的是性能问题。因为我将每三个月在这三张 map 上进行一次设置,所以很好。但是每秒每1000个请求中的主应用程序代码就会出现三张 map ,所以我想变得非常快。
最初,我正在考虑删除此if语句-
// how to avoid this?
if (allset) {
throw new IllegalStateException("All the maps are already set");
}
但是我怀疑,这将无法正常工作,因为在我更新映射时,线程之间的映射会不匹配吗?
这就是我从主应用程序线程中从
ClientData class
读取值的方式-String data1 = ClientData.getPrimaryMapping().get(some_value1).get(some_value2);
String data2 = ClientData.getSecondaryMapping().get(some_value1).get(some_value3);
String data3 = ClientData.getTertiaryMapping().get(some_value1).get(some_value4);
更新:-
满足以上所有条件的另一种使用
CountDownLatch
的解决方案-以下是我在其中使用
ClientData
的CountDownLatch
类-public class ClientData {
public static class Mappings {
public final Map<String, Map<Integer, String>> primary;
public final Map<String, Map<Integer, String>> secondary;
public final Map<String, Map<Integer, String>> tertiary;
public Mappings(
Map<String, Map<Integer, String>> primary,
Map<String, Map<Integer, String>> secondary,
Map<String, Map<Integer, String>> tertiary
) {
this.primary = primary;
this.secondary = secondary;
this.tertiary = tertiary;
}
}
private static final AtomicReference<Mappings> mappings = new AtomicReference<>();
private static final CountDownLatch hasBeenInitialized = new CountDownLatch(1);
public static Mappings getMappings() {
try {
hasBeenInitialized.await();
return mappings.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException(e);
}
}
public static void setMappings(
Map<String, Map<Integer, String>> primary,
Map<String, Map<Integer, String>> secondary,
Map<String, Map<Integer, String>> tertiary
) {
setMappings(new Mappings(primary, secondary, tertiary));
}
public static void setMappings(Mappings newMappings) {
mappings.set(newMappings);
hasBeenInitialized.countDown();
}
}
我将在这样的主应用程序线程中使用
ClientData
类-Mappings mappings = ClientData.getMappings();
// use mappings.primary
// use mappings.secondary
// use mappings.tertiary
此代码会对性能产生影响吗?简而言之,哪一种更好,我应该使用
ReentrantReadWriteLock
还是上述CountDownLatch
一种解决方案?所以现在的问题是,
CountDownLatch
解决方案与ReentrantReadWriteLock
解决方案?在高读/低写用例中,哪一个性能更好?如果可能的话,有人可以在我上面的解决方案中提供一个使用
ReentrantReadWriteLock
的示例吗?这样一来,我就可以将性能与CountDownLatch
解决方案和ReentrantReadWriteLock
解决方案的性能进行比较。由于我无法为上述用例提供一个使用
ReentrantReadWriteLock
的解决方案。注意:-
就我而言,写入将每三个或四个月发生一次。但是读取会以每秒
1000
请求的非常高的速度从多个线程进行。因此它必须非常快。
最佳答案
您应该通过引入Mappings
继续进行重组。您不需要单独管理这三个引用(这变得很复杂)。而是管理一个引用。
class ClientData {
// This is immutable after creation (like your OP)
class Mappings { /* definition from your original post */ }
// this should be volatile;
private static volatile Mappings instance;
// the read path proceeds without acquiring any lock at all. Hard to
// get faster than a volatile read. Note the double-checked locking pattern
// works JDK 6 or greater when using volatile references (above)
public static Mappings getMappings() {
Mappings result = instance;
if(result == null) {
synchronized(ClientData.class) {
result = instance;
// recall while() is required to handle spurious wakeup
while(result == null) {
ClientData.class.wait();
result = instance;
}
}
}
}
public static setMappings(Map one, Map two, Map three) {
synchronized(ClientData.class) {
instance = new Mappings(one,two,three);
ClientData.class.notifyAll()
}
}
}
我认为这样做有以下好处:
不幸的是(IMHO)Java没有内置良好的“Waitable Reference”。但是,人们不能要求一切!第三方库有一些支持-Guava的
Suppliers.memoize()
是一个不错的起点。祝您的项目好运。
关于java - 如何在使用RentrantLock完成所有三个 map 的初始化之前避免读取,并在更新完成后返回更新的 map 集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23369069/