2014-08-27 70 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中有,你可以用,也有些鎖類,但往往你什麼都不需要那麼花哨。