2014-08-27 72 views
0

我面临这个问题,我有两个类A和B,'A类'有两个线程t1和t2,它试图访问'B类'地图如下,现在的问题是,我无法对B类地图进行任何更改,因此我无法使用synchronised关键字,或者我无法将其置于同步方法中。所以我的疑问是,有没有其他的方式来同步这张地图。多线程访问一个不同步的地图

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()); 
     }  
    } 

我使用LinkedHashMap,仅跟踪插入的顺序(忽略性能问题)

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; 
    } 
} 
+1

正如我所看到的,您可以选择从B公开的setMap,只需在A中创建并发hashmap,然后将其设置为B. – Adi 2014-08-27 17:53:52

回答

0

简单:

public class A{ 

    static B b; 
    static Map bMap; 

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

    ... //now use bMap instead 
} 
+0

OP声明_“我无法对B类地图进行任何更改”_ – Scis 2014-08-27 17:57:09

+0

我的错,修正了它 – J4v4 2014-08-27 18:00:18

2

您可以创建同步Map一次兼得线程使用那个Map实例。

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"); 
    } 
}); 
1

为什么不直接设置地图的B.如果你不能使用ConcurrentHashMap,那么你可以使用Collections.synchronizedMap(new LinkedHashMap<>())代替。任何外部同步总是会导致对地图的不同步访问。


public class A{ 

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

'synchronizedMap'有相同的可能性:例如,从它获取的'keySet.iterator()'不同步,因此它不是线程安全的,需要外部同步。 'ConcurrentMap'在这方面要好得多,可以保证迭代器获取/首次调用next()/ hasNext()时存在一个稳定的迭代器。 – 2014-08-27 18:13:39

+0

是ConcurrentHashMap好多了,我只添加了LinkedHashMap示例,因为op声明'我正在使用LinkedHashMap,仅用于跟踪插入顺序',这对于CHM是不可能的。 – 2014-08-28 08:38:28

1

正如其他指示,则可以使用一个同步包装,并java.util.Collections提供了一个方便的手段,以获得一个。但是,让每个线程都获得自己的同步包装并不能帮助您 - 所有参与者都需要使用同一个同步工具。

更一般地说,如果您需要同步访问某些不太方便的对象,那么您可以使用外部同步。在你的情况,可能是这样的:

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() 
      } 
     }); 

     // ... 
    } 
} 

包java.util.concurrent.locks中有,你可以用,也有些锁类,但往往你什么都不需要那么花哨。