我正在尝试实现锁定,当我在我的三个地图上进行写入时,我希望通过该锁定来避免发生读取。所以我的要求是——
- 读取阻塞,直到第一次设置所有三个映射。
- 现在第二次,如果我要更新地图,我仍然可以返回所有三个旧地图值(在所有三个地图上完成更新之前),或者它应该阻止并在更新完成时返回所有新的三个地图值在所有三张地图上。
因为我有三个 Maps - primaryMapping
,secondaryMapping
所以tertiaryMapping
它应该返回三个更新地图的所有新值,或者它应该返回地图的所有旧值。基本上,在更新时我不想返回primaryMapping
旧值、secondaryMapping
新值和tertiaryMapping
新值。
它应该是一致的,要么应该返回旧值,要么应该在更新地图后返回新值。就我而言,地图更新将在三个月或四个月内发生一次。
下面是我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
类变量中那三张地图。
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
问题陈述:-
在我的代码库中,如上所示ClientData
,我想,一旦第一次设置地图,我将无法更新地图,因为这条线会导致问题,它会抛出异常,然后还有如何如上所示实施我的第二点?
// how to avoid this?
if (allset) {
throw new IllegalStateException("All the maps are already set");
}
我怎样才能成功地实现以上两点?我想,我在这里遗漏了一些非常小的东西?我想在这里使用 ReentrantLock,但也欢迎任何其他建议。我主要关心的是性能问题。因为我将在三个月内对这三张地图进行一次设置,所以这很好。但是每秒每 1000 个请求就会从主应用程序代码中获取三个地图,所以我想要非常快。
最初,我想删除这个 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
每秒请求数。所以它必须非常快。