0

I read in Book 4.3. Delegating thread safety in Concurrency in Practice use of final and Collections.unmodifiableMap(map) How does DelegatingVehicleTracker (p. 65 Goetz) return a "live" view? to delegate thread safety and i tried to create my example and saw the changes made by one thread are not getting reflected when map is returned.I am doing anything wrong

public class MapDem {

final Map<Integer, Integer> map;

final Map<Integer, Integer> map1;

public MapDem() {
    map = new HashMap<Integer, Integer>();
    map.put(1, 10);
    map.put(2, 20);
    map1 = Collections.unmodifiableMap(map);
}

public Map<Integer, Integer> getMap() {
    return Collections.unmodifiableMap(new HashMap<Integer, Integer>(map));
}

public void setValue(int key,int value){
    map.replace(key, value);
}


public static void main(String args[]) {
    MapDem demo = new MapDem();

    Thread t3 = new Thread(new Runnable() {

        @Override
        public void run() {
            System.out.println(demo.getMap());

        }

    });
    t3.start();

    Thread t4 = new Thread(new Runnable() {

        @Override
        public void run() {
            demo.setValue(2, 40);

        }

    });
    t4.start();

    try {
        t3.join();
        t4.join();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    System.out.println(demo.getMap().size());

   }

 }

The output coming is

{1=10, 2=20}
 2
 or 
{1=10, 2=40}
 2

I want the map to see the updated value always.

Community
  • 1
  • 1
coder25
  • 2,363
  • 12
  • 57
  • 104

2 Answers2

0

Sorry I had misread your code in my earlier answer.

To return a "live" view, you must share the underlying map with other threads (after all that's the definition of "live". You want to see updates done by other threads). What implementation you use for this shared map (be it synchronized or ConcurrentHashMap) is irrelevant. The wrapping with unmodifiable has nothing to do with concurrency - it is simply to make the map a "view", i.e. read-only.

All of this won't make it so that t3 will execute before t4. For that you need to introduce a happens-before relationship between them. The easiest way to do it here is to first join on t4 and then start t3.

Enno Shioji
  • 26,542
  • 13
  • 70
  • 109
  • That i understand but n real scenarios that kind of join i cant take it.I want a solution so that map always reads the updated values – coder25 Dec 27 '16 at 13:50
  • You need to think of this in case that Thread 1 always read actual values of map. It won't wait for updated values, unless you will implement some kind of blocker which will know old values and then read only after change. In this example it is not deterministic if Thread 1 will execute before thread 2 – Kamil Banaszczyk Dec 27 '16 at 13:52
  • http://stackoverflow.com/questions/33532745/how-does-delegatingvehicletracker-p-65-goetz-return-a-live-view.Please refer to the link i am refering this scenerio – coder25 Dec 27 '16 at 13:54
0

I think you don't quite understand what it is about. Or maybe I, but i will try to show you what i'm thinking. This is not about waiting for updated value, but allowing changes only in one object, while reading can be done from others. Take a look at my code:

public class DelegatingVehicleTracker {
    private final ConcurrentMap<String, String> locations;
    private final Map<String, String> unmodifiableMap;

    public DelegatingVehicleTracker(Map<String, String> points) {
        locations = new ConcurrentHashMap<String, String>(points);
        unmodifiableMap = Collections.unmodifiableMap(locations);
    }

    public Map<String, String> getLocations() {
        return unmodifiableMap;
    }

    public String getLocation(String id) {
        return locations.get(id);
    }

    public void setLocation(String id, int x, int y) {
        if (locations.replace(id, x + y + "") == null)
            throw new IllegalArgumentException("invalid vehicle name: " + id);
    }

    public static void main(String[] args) {

        HashMap<String, String > vehicles= new HashMap<>();

        vehicles.put("1", "1");

        DelegatingVehicleTracker tracker = new DelegatingVehicleTracker(vehicles);

        Map<String, String> unmodifiableMap = tracker.getLocations();


        new Thread(() -> {
            while(true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(unmodifiableMap);
            }
        }).start();

        new Thread(() -> {
            int i=0;
            while(true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                tracker.setLocation("1",i+1, i+2);
                i++;
            }
        }).start();



    }

}

In main method i'm getting unmodifiable map from object tracker, and one of my threads is using it to display values in this map. It will always show current values, so if it will execute "sout" before other thread will execute set Location, values will be "old", but you need to see that this is not "old" value, it is current value as it is in collection while executing "sout". As I said in comment if you want to see updated value, you need to wait for update, and this is another scenario. Thread safe here is that you can throw away collection to everybody, but they will only have permission to read this map, so you don't have direct access to the collection. You can only execute setLocation through DelegatingVehicleTracker.

Kamil Banaszczyk
  • 1,133
  • 1
  • 6
  • 23