此类中引用的Map是否安全发布?

时间:2015-10-02 08:16:44

标签: java multithreading synchronization thread-safety

调用IsItThreadsafe.getValue(...)的线程是否会从变量IsItThreadsafe.map引用的Map中获取最新值(假设在IsItThreadSafe.main(...)中实现了确切的使用场景?

public class IsItThreadSafe {

    private Map<String, String> map; 

    public synchronized void setMap(Map<String, String> map) {
        this.map = map;
    }

    public synchronized String getValue(String key) {
        if (this.map == null) {
            this.map = new HashMap<>();
        }
        return map.get(key);
    }


    public static void main(String[] args) throws Exception {
        IsItThreadSafe sharedObject = new IsItThreadSafe();
        Thread t1 = new Thread(() -> {
            while (true) {
                for (int i = 0; i < 10; i++) {
                    String key = String.valueOf(i);
                    String value = sharedObject.getValue(key);

                    if (value!=null) {
                        System.out.println("key = " + key + " value = " + value);
                    }
                }
            }
        });
        t1.start();

        Thread t2 = new Thread(() -> {
            while (true) {
                Map<String, String> mappedData = new HashMap<>();

                for (int i = 0; i < 10; i++) {
                    mappedData.put(String.valueOf(i), String.valueOf(i + 1));
                }

                sharedObject.setMap(mappedData);
            }
        });
        t2.start();

        t1.join();
        t2.join();
    }
}

1 个答案:

答案 0 :(得分:1)

除非setMap调用者继续修改传递给setMap的地图,否则它应该正常工作。一旦setMap被调用,它应该忘记这个地图。这样的版本一般会更安全:

public synchronized void setMap(Map<String, String> map) {
    this.map = new HashMap<>(map);
}

当然,虽然您的代码是正确的,但并不意味着代码是最佳的。特别是如果您不打算将新值放入地图中,则每次查询值时都可以摆脱同步。例如,您可以使用AtomicReference

private final AtomicReference<Map<String, String>> map = new AtomicReference<>(); 

public void setMap(Map<String, String> map) {
    this.map.set(new HashMap<>(map));
}

public String getValue(String key) {
    Map<String, String> m = this.map.get();
    // loop is necessary if setMap(null) call occurs
    while (m == null) {
        // Use CAS to avoid possible overwrite of concurrent setMap() call
        m = new HashMap<>();
        if(!this.map.compareAndSet(null, m))
            m = this.map.get();
    }
    return m.get(key);
}

这里同步被volatile读取取代,这通常要快得多。