我试图实现锁,每当我对三个映射进行写入时,都要避免读取。所以我的要求是-
因为我有三个映射- 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次请求,从主应用程序代码中得到三个映射,所以我想要非常快。
一开始,我想把这句话删除-
// 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请求)从多个线程进行。所以它必须非常快。
发布于 2014-05-07 04:41:56
您应该按照引入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()
}
}
}我认为这有以下好处:
java没有内置一个好的“”,这有点不幸(IMHO)。但是一个人不能要求所有的东西!第三方库有一些支持
祝你的项目好运。
发布于 2014-04-29 22:14:53
您正确地认为需要删除if (allset)块。否则你将无法更新。
因此,看起来您必须解决可能发生的以下问题:
ClientData.getPrimaryMapping()ClientData.getSecondaryMapping() -现在你有了不匹配的数据我将创建一个单独的方法来同时获取所有映射,而不是在两者之间调用所有的映射方法。该方法应该返回映射的列表(或数组或其他类型的容器)。通过将锁移动到此方法,您可以确保在获得地图时不会有任何更新。
例如,类似这样的东西:
// create a new list on each update
private static List<Map<String, Map<Integer, String>>> mappings;
public static final List<Map<String, Map<Integer, String>>> getAllMappings() {
lock.lock();
try {
while (!allset) {
allsetnow.await();
}
return mappings;
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // reset interruptedd state.
throw new IllegalStateException(e);
} finally {
lock.unlock();
}
}我想它也可能更快,因为更少的方法调用和更少的尝试捕获块,但如果你真的关心性能,你应该衡量什么表现最好。
发布于 2014-04-30 10:12:39
如果您有这样的代码块
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);在getMapping方法中,您无法修复可能的竞赛。data1、data2和data3的值不一致,无论在被调用的方法中同步得有多好(一旦允许更新),这三行之间总是可以进行更新。
解决这个问题的唯一方法是将同步逻辑移到调用方中。在上面的示例代码中,所有三条语句都必须放在同一个受保护的块中。好消息是,这将有助于您的性能,因为如果您正确地这样做,您就不需要在被调用的方法中锁定。因此,与其三次锁定和解锁,不如只对整个块执行一次。
对于大量读取和偶尔更新的用例而言,使用允许并发读取的ReadWriteLock是一种自然的选择。
https://stackoverflow.com/questions/23369069
复制相似问题