Java 当我写东西时,如何防止读发生?

Java 当我写东西时,如何防止读发生?,java,multithreading,countdownlatch,atomicreference,Java,Multithreading,Countdownlatch,Atomicreference,我正在尝试实现一个锁,我不希望每次写操作时都发生读操作 下面是我的ClientData类,我在其中使用CountDownLatch- public class ClientData { private static final AtomicReference<Map<String, Map<Integer, String>>> primaryMapping = new AtomicReference<>(); private st

我正在尝试实现一个锁,我不希望每次写操作时都发生读操作

下面是我的
ClientData
类,我在其中使用
CountDownLatch
-

public class ClientData {

    private static final AtomicReference<Map<String, Map<Integer, String>>> primaryMapping = new AtomicReference<>();
    private static final AtomicReference<Map<String, Map<Integer, String>>> secondaryMapping = new AtomicReference<>();
    private static final AtomicReference<Map<String, Map<Integer, String>>> tertiaryMapping = new AtomicReference<>();

    // should this be initialized as 1?
    private static final CountDownLatch hasBeenInitialized = new CountDownLatch(1) 

    public static Map<String, Map<Integer, String>> getPrimaryMapping() {
        try {
            hasBeenInitialized.await();
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }

        return primaryMapping.get();
    }

    public static void setPrimaryMapping(Map<String, Map<Integer, String>> map) {
        primaryMapping.set(map);
        hasBeenInitialized.countDown();
    }

    public static Map<String, Map<Integer, String>> getSecondaryMapping() {
        try {
            hasBeenInitialized.await();
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }

        return secondaryMapping.get();
    }       

    public static void setSecondaryMapping(Map<String, Map<Integer, String>> map) {
        secondaryMapping.set(map);
        hasBeenInitialized.countDown();
    }

    public static Map<String, Map<Integer, String>> getTertiaryMapping() {
        try {
            hasBeenInitialized.await();
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }

        return tertiaryMapping.get();
    }       

    public static void setTertiaryMapping(Map<String, Map<Integer, String>> map) {
        tertiaryMapping.set(map);
        hasBeenInitialized.countDown();
    }       
}
设置完这些原子引用后,其他一些线程必须从这些原子引用中读取数据

更新:-

下面是我的后台线程代码,它将从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 which can be
        // used by other threads
    ClientData.setPrimaryMapping(primaryTables);
    ClientData.setSecondaryMapping(secondaryTables);
    ClientData.setTertiaryMapping(tertiaryTables);
    }
}
公共类临时调度器{
private final ScheduledExecutorService scheduler=Executors.newScheduledThreadPool(1);
公共无效startScheduler(){
final ScheduledFuture taskHandle=scheduler.scheduleAtFixedRate(new Runnable()){
公开募捐{
试一试{
callServers();
}捕获(例外情况除外){
例如printStackTrace();
}
}
},0,10,时间单位(分钟);
}
}
//调用服务器获取数据,然后解析
//回应。
私有void调用服务器(){
字符串url=“url”;
RestTemplate RestTemplate=新RestTemplate();
String response=restTemplate.getForObject(url,String.class);
解析响应(response);
}
//解析响应并将其存储在变量中
私有空解析响应(字符串响应){
//...       
ConcurrentHashMap primaryTables=null;
ConcurrentHashMap secondaryTables=null;
ConcurrentHashMap tertiaryTables=null;
//...
//将数据存储在ClientData类变量中,这些变量可以
//被其他线程使用
setPrimaryMapping(primaryTables);
ClientData.SetSecondary映射(第二个表);
settertialMapping(tertiaryTables);
}
}

如果您想独立处理所有3个变量(即获取第三个变量不需要等待主变量被设置),这就是我阅读您的问题的方式,您只需要为每个映射创建一个倒计时锁存器。每个设置器对所设置变量的相应锁存进行倒计时。每个getter调用都会在各自的闩锁上等待。

此设置完全是一种过激的设置

下面是一个工作正常且更简单的替代方案:

public class MappingContainer {
     private final Map<String, Map<Integer, String>> primaryMapping;
     private final Map<String, Map<Integer, String>> secondaryMapping;
     private final Map<String, Map<Integer, String>> tertiaryMapping;

     // + constructor and getters
}

public class ClientData {
    private static volatile MappingContainer mappingContainer;

    // regular setters and getters
}

public class TempScheduler {
//...
    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 which can be
        // used by other threads
        ClientData.setMappingContainer( new MappingContainer( primaryTables, secondaryTables, tertiaryTables );
    }
}
公共类映射容器{
私有最终映射主映射;
私有最终映射二次映射;
私有最终地图三元映射;
//+构造函数和getter
}
公共类ClientData{
私有静态易失性映射容器映射容器;
//常规二传手和接球手
}
公共类临时调度器{
//...
私有空解析响应(字符串响应){
//...       
ConcurrentHashMap primaryTables=null;
ConcurrentHashMap secondaryTables=null;
ConcurrentHashMap tertiaryTables=null;
//...
//将数据存储在ClientData类变量中,这些变量可以
//被其他线程使用
setMappingContainer(新的MappingContainer(primaryTables、secondaryTables、tertiaryTables);
}
}

锁存器和原子引用应该留待更简单的构造无法将其剪切时使用。特别是,如果必须计算任意N个事件(而不是3个特定事件),则锁存器是很好的选择,原子引用只有在使用比较和设置或获取和设置习惯用法时才有用。

在此处使用
synchronized
关键字不是更容易、更可读吗?请发布入口点方法
main
的代码。您考虑过吗?这正是它的设计目的。if
ClientData.setPri呢maryMapping(主表);
同时被调用了三次?@Braj:它肯定不会同时被调用三次。我已经更新了设置这三个原子引用的代码。在我的情况下,如果
setPrimaryMapping
发生任何写操作,我需要等待
getPrimaryMapping
。同样,我需要等待
getSecondaryMapping
如果有任何写入发生在
SetSecondary mapping
上。此外,如果有任何写入发生在
SetterialMapping
上,我需要等待
GetterialMapping
上。因此,在这种情况下,它也应该设置为1?如果是,那么您可以解释为什么仅就我所知?您需要为每个映射设置一个倒计时锁存器(因为它们都是独立跟踪的)。这意味着3个倒计时闩锁,所有值均为1。该值必须为1,因为您只等待一个倒计时事件发生。现在有意义。有没有办法,我可以使用一个倒计时闩锁而不是三个?我想,为此,我可能需要更改构造ClientData对象的方式。对吗?如果您想跟踪3 events相互独立,它将需要3个锁存器。如果更改为使用一个setter方法,该方法接受所有3个映射(直接或使用@bizclop建议的包装类)设置后,您只需要一个锁存器就可以倒计时。谢谢。在您的示例中,Volatile在这里有什么帮助?假设我启动主应用程序,那么它是否会等待get调用?因为set调用可能需要一些时间,因为它是由后台线程完成的,所以我们不知道需要多长时间o解析响应并将其设置在ClientData类中?因此,在这种情况下,第一次get调用将失败,对吗?我明白了,这与以前略有不同。如果要在调用setter之前阻止getter,请使用闩锁而不是
volatile
关键字。只需一个闩锁,从1开始倒数(因为您希望setter被调用一次)。一个更好的解决方案是,只有在所有表都可用时,才构建
ClientData
对象本身。至少我们在某些方面达成了一致。:)。在我的例子中,我有三个不同的
public class MappingContainer {
     private final Map<String, Map<Integer, String>> primaryMapping;
     private final Map<String, Map<Integer, String>> secondaryMapping;
     private final Map<String, Map<Integer, String>> tertiaryMapping;

     // + constructor and getters
}

public class ClientData {
    private static volatile MappingContainer mappingContainer;

    // regular setters and getters
}

public class TempScheduler {
//...
    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 which can be
        // used by other threads
        ClientData.setMappingContainer( new MappingContainer( primaryTables, secondaryTables, tertiaryTables );
    }
}