Soumyaansh
Soumyaansh

Reputation: 8988

Multiple Threads accessing a non synchronised Map

I am facing this issue, I have two classes A and B , 'class A' has two Threads t1 and t2 and its trying to access 'Class B' map as below now the problem is, I can not make any changes on class B' map hence I can not use synchronised keyword or I can not put it in synchronised method. So my doubt is, is there any other way to synchronise this map.

public class A{

    static B b = new B();

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {

                Map<Integer,String> map = Collections.synchronizedMap(b.getMap());
                map.put(1, "one");
                map.put(2, "two");
                map.put(3, "three");
                b.setMap(map);
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {

                Map<Integer,String> map = Collections.synchronizedMap(b.getMap());
                map.put(4, "four");
                map.put(5, "five");
                map.put(6, "six");
                b.setMap(map);

            }
        });

        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


        for(Map.Entry<Integer, String> entry : b.getMap().entrySet()){
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }     
    }

I am using LinkedHashMap, only to track the order of insertion (ignore performance issues)

public class B{

    private Map<Integer,String> map = new LinkedHashMap<Integer,String>();

    public Map<Integer,String> getMap() {
        return map;
    }

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

Upvotes: 0

Views: 251

Answers (4)

John Bollinger
John Bollinger

Reputation: 180161

As others indicated, you can use a synchronized wrapper, and java.util.Collections provides a convenient means to obtain one. Having each thread get its own synchronized wrapper does not help you, though -- all participants need to use the same synchronization tool.

More generally, if you need to synchronize access to some less convenient object then you can use external synchronization. In your case, that might look like this:

public class A{

    static B b = new B();
    // will use this object to synchronize access to b's map:
    static Object bLock = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                Map<Integer,String> map = b.getMap();

                synchronized(bLock) {
                    map.put(1, "one");
                    map.put(2, "two");
                    map.put(3, "three");
                }
                // no need for setMap()
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                Map<Integer,String> map = b.getMap();

                synchronized (bLock) {
                    map.put(4, "four");
                    map.put(5, "five");
                    map.put(6, "six");
                }
                // no need for setMap()
            }
        });

        // ...
    }
}

Package java.util.concurrent.locks has some lock classes that you can use, too, but often you don't need anything so fancy.

Upvotes: 1

Leonard Br&#252;nings
Leonard Br&#252;nings

Reputation: 13222

Why not just set the map in B. If you cant use ConcurrentHashMap then you can use the Collections.synchronizedMap(new LinkedHashMap<>()) instead. Any external synchronization will always leave the possibility of unsynchronized access to the map.


public class A{

    static B b;
    static {
        b = new B();
        b.setMap(new ConcurrentHashMap());
    }
    //...
}

Upvotes: 1

tobii
tobii

Reputation: 517

You can create the synchronized Map once and have both threads use that one Map instance.

final Map<Integer,String> map = Collections.synchronizedMap(b.getMap());
b.setMap(map);

Thread t1 = new Thread(new Runnable() {

    @Override
    public void run() {
        map.put(1, "one");
        map.put(2, "two");
        map.put(3, "three");
    }
});

Thread t2 = new Thread(new Runnable() {
    @Override
    public void run() {
        map.put(4, "four");
        map.put(5, "five");
        map.put(6, "six");
    }
});

Upvotes: 2

J4v4
J4v4

Reputation: 810

Simply:

public class A{

    static B b;
    static Map bMap;

    static {
        b = new B();
        bMap = Collections.synchronizedMap(b.getMap());
    }

    ... //now use bMap instead
}

Upvotes: 0

Related Questions